Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ Specify `hideSocial` to hide the ability to sign in with a social provider.
<LoginForm hideSocial={true} />
```

Specify `endpoint` to send the form data to a custom endpoint for logging in.

```html
<LoginForm endpoint="/customLogin" />
```

Customize the form by providing your own markup.

```html
Expand Down Expand Up @@ -349,7 +355,13 @@ Specify `hideSocial` to hide the ability to register with a social provider.
<RegistrationForm hideSocial={true} />
```

Customize the form by providing your own markup.
Specify `endpoint` to send the form data to a custom endpoint for registration.

```html
<RegistrationForm endpoint="/customRegister" />
```

Customize the form by providing your own markup.

By default, the registration form will render these four fields, and they will be required by the user: `givenName`, `surname`, `email`, and `password`. Express.js users who want to make `givenName` and/or `surname` optional, or to add new required fields (like `username`), can refer to [Stormpath Express Library Guide](https://docs.stormpath.com/nodejs/express/latest/registration.html).

Expand Down Expand Up @@ -473,6 +485,13 @@ Renders a password reset form.
<ResetPasswordForm />
```

Specify `endpoint` to send the form data to a custom endpoint for initiating the reset
password process.

```html
<ResetPasswordForm endpoint="/customReset" />
```

Customize the form by providing your own markup.

```html
Expand Down Expand Up @@ -534,6 +553,12 @@ Renders a change password form. The parameter `spToken` is required in order for
<ChangePasswordForm spToken={this.props.location.query.sptoken} />
```

Specify `endpoint` to send the form data to a custom endpoint for changing the password.

```html
<ChangePasswordForm endpoint="/customChange" />
```

Customize the form by providing your own markup.

```html
Expand Down Expand Up @@ -598,6 +623,12 @@ Renders a form that allows you to update the user profile.
<UserProfileForm />
```

Specify `endpoint` to send the form data to a custom endpoint for updating the user profile.

```html
<UserProfileForm endpoint="/customMe" />
```

**Important:** In order to update user data, you need to provide your own POST API for the `me` endpoint.

Using the `express-stormpath` library, simply expose a new endpoint as shown below.
Expand Down
52 changes: 44 additions & 8 deletions src/actions/UserActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,85 @@ function dispatch(event) {
}, 0);
}

function resolveActionParams(options, settings, callback) {
if (typeof options === 'function') {
return [{}, {}, callback];
}

if (typeof settings === 'function' && typeof callback === 'undefined') {
return [options, {}, callback];
}

return [options, settings, callback];
}

class UserActions {
login(options, callback) {
login(options, settings, callback) {
[options, settings, callback] = resolveActionParams(options, settings, callback);

dispatch({
type: UserConstants.USER_LOGIN,
options: options,
settings: settings,
callback: callback
});
}

register(options, callback) {
register(options, settings, callback) {
[options, settings, callback] = resolveActionParams(options, settings, callback);

dispatch({
type: UserConstants.USER_REGISTER,
options: options,
settings: settings,
callback: callback
});
}

forgotPassword(options, callback) {
forgotPassword(options, settings, callback) {
[options, settings, callback] = resolveActionParams(options, settings, callback);

dispatch({
type: UserConstants.USER_FORGOT_PASSWORD,
options: options,
settings: settings,
callback: callback
});
}

verifyEmail(spToken, callback) {
verifyEmail(spToken, settings, callback) {
[options, settings, callback] = resolveActionParams(options, settings, callback);

dispatch({
type: UserConstants.USER_VERIFY_EMAIL,
options: {
spToken: spToken
},
settings: settings,
callback: callback
});
}

changePassword(options, callback) {
changePassword(options, settings, callback) {
[options, settings, callback] = resolveActionParams(options, settings, callback);

dispatch({
type: UserConstants.USER_CHANGE_PASSWORD,
options: options,
settings: settings,
callback: callback
});
}

updateProfile(data, callback) {
updateProfile(data, settings, callback) {
[options, settings, callback] = resolveActionParams(options, settings, callback);

dispatch({
type: UserConstants.USER_UPDATE_PROFILE,
options: {
data: data
},
settings: settings,
callback: callback
});
}
Expand All @@ -69,10 +99,16 @@ class UserActions {
});
}

logout(callback) {
logout(settings, callback) {
if (typeof settings === 'function' && typeof callback === 'undefined') {
callback = settings;
settings = {};
}

dispatch({
type: UserConstants.USER_LOGOUT,
callback: callback
callback: callback,
settings: settings
});
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,25 +79,25 @@ class App extends EventEmitter {
let appReducer = (payload) => {
switch(payload.type) {
case UserConstants.USER_LOGIN:
userStore.login(payload.options, payload.callback);
userStore.login(payload.options, payload.settings, payload.callback);
break;
case UserConstants.USER_LOGOUT:
userStore.logout(payload.callback);
userStore.logout(payload.settings, payload.callback);
break;
case UserConstants.USER_REGISTER:
userStore.register(payload.options, payload.callback);
userStore.register(payload.options, payload.settings, payload.callback);
break;
case UserConstants.USER_FORGOT_PASSWORD:
userStore.forgotPassword(payload.options, payload.callback);
userStore.forgotPassword(payload.options, payload.settings, payload.callback);
break;
case UserConstants.USER_CHANGE_PASSWORD:
userStore.changePassword(payload.options, payload.callback);
userStore.changePassword(payload.options, payload.settings, payload.callback);
break;
case UserConstants.USER_UPDATE_PROFILE:
userStore.updateProfile(payload.options.data, payload.callback);
userStore.updateProfile(payload.options.data, payload.settings, payload.callback);
break;
case UserConstants.USER_VERIFY_EMAIL:
userStore.verifyEmail(payload.options.spToken, payload.callback);
userStore.verifyEmail(payload.options.spToken, payload.settings, payload.callback);
break;
case TokenConstants.TOKEN_SET:
userService.setToken(payload.options.type, payload.options.token);
Expand Down
17 changes: 13 additions & 4 deletions src/components/ChangePasswordForm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { PropTypes } from 'react';
import { Link } from 'react-router';

import utils from '../utils';
Expand Down Expand Up @@ -48,6 +48,10 @@ class DefaultChangePasswordForm extends React.Component {
}

export default class ChangePasswordForm extends React.Component {
static propTypes = {
endpoint: PropTypes.string
};

state = {
spToken: null,
fields: {
Expand All @@ -72,7 +76,7 @@ export default class ChangePasswordForm extends React.Component {
e.preventDefault();
e.persist();

var next = (err, data) => {
var next = (err, data, headers = {}) => {
if (err) {
return this.setState({
isFormProcessing: false,
Expand All @@ -91,7 +95,12 @@ export default class ChangePasswordForm extends React.Component {
});
}

UserActions.changePassword(data, (err) => {
const settings = {
endpoint: this.props.endpoint,
headers: headers
};

UserActions.changePassword(data, settings, (err) => {
if (err) {
if (err.status === 404) {
err.message = 'The reset password token is not valid. Please try resetting your password again.';
Expand Down Expand Up @@ -186,7 +195,7 @@ export default class ChangePasswordForm extends React.Component {

render() {
if (this.props.children) {
let selectedProps = utils.excludeProps(['onSubmit', 'children', 'spToken'], this.props);
let selectedProps = utils.excludeProps(['onSubmit', 'children', 'spToken', 'endpoint'], this.props);

return (
<form onSubmit={this.onFormSubmit.bind(this)} {...selectedProps}>
Expand Down
18 changes: 13 additions & 5 deletions src/components/LoginForm.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { PropTypes } from 'react';
import { Link } from 'react-router';

import utils from '../utils';
Expand Down Expand Up @@ -143,7 +143,11 @@ class DefaultLoginForm extends React.Component {

export default class LoginForm extends React.Component {
static contextTypes = {
router: React.PropTypes.object.isRequired
router: PropTypes.object.isRequired
};

static propTypes = {
endpoint: PropTypes.string
};

state = {
Expand All @@ -170,7 +174,7 @@ export default class LoginForm extends React.Component {
});
};

var next = (err, data) => {
var next = (err, data, headers = {}) => {
if (err) {
if (onSubmitError) {
return onSubmitError({
Expand All @@ -185,11 +189,15 @@ export default class LoginForm extends React.Component {
// If the user didn't specify any data,
// then simply default to what we have in state.
data = data || this.state.fields;
const settings = {
endpoint: this.props.endpoint,
headers: headers
}

UserActions.login({
login: data.username,
password: data.password
}, (err, result) => {
}, settings, (err, result) => {
if (err) {
if (onSubmitError) {
return onSubmitError({
Expand Down Expand Up @@ -289,7 +297,7 @@ export default class LoginForm extends React.Component {

render() {
if (this.props.children) {
let selectedProps = utils.excludeProps(['redirectTo', 'hideSocial', 'onSubmit', 'onSubmitError', 'onSubmitSuccess', 'children'], this.props);
let selectedProps = utils.excludeProps(['redirectTo', 'hideSocial', 'onSubmit', 'onSubmitError', 'onSubmitSuccess', 'children', 'endpoint'], this.props);

return (
<form onSubmit={this.onFormSubmit.bind(this)} {...selectedProps}>
Expand Down
12 changes: 8 additions & 4 deletions src/components/LogoutLink.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React from 'react';
import React, { PropTypes } from 'react';

import utils from './../utils';
import context from './../context';
import UserActions from './../actions/UserActions';

export default class LogoutLink extends React.Component {
static contextTypes = {
router: React.PropTypes.object.isRequired
router: PropTypes.object.isRequired
};

static propTypes = {
endpoint: PropTypes.string
};

state = {
Expand All @@ -30,14 +34,14 @@ export default class LogoutLink extends React.Component {
if (!this.state.disabled) {
this.setState({ disabled: true });

UserActions.logout(() => {
UserActions.logout({ endpoint: this.props.endpoint }, () => {
this._performRedirect(primaryRedirectTo);
});
}
}

render() {
var selectedProps = utils.excludeProps(['redirectTo', 'href', 'onClick', 'disabled', 'children'], this.props);
var selectedProps = utils.excludeProps(['redirectTo', 'href', 'onClick', 'disabled', 'children', 'endpoint'], this.props);

return (
<a href='#' onClick={this.onClick.bind(this)} disabled={this.state.disabled} {...selectedProps}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/LogoutRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import UserActions from './../actions/UserActions';
export default class LogoutRoute extends Route {
static defaultProps = {
onEnter(nextState, replace, callback) {
UserActions.logout(() => {
UserActions.logout({ endpoint: this.props.endpoint }, () => {
var router = context.getRouter();
var homeRoute = router.getHomeRoute();
var loginRoute = router.getLoginRoute();
Expand Down
Loading