Simple and intuative accounts view layer for react in meteor
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
__tests__
lib
.eslintrc
.gitignore
README.md
index.js
package-lock.json
package.js
package.json

README.md

Meteor Accounts UI for React

meteor add meteoreact:accounts

A huge credit goes to the useraccounts package and the people behind it.

This package has been created to be used in one of my projects which was purely React. Although the original useraccounts package can be used in react, it depends on blaze and jquery which are both useless when developing with react.

Right now, you might find that there are several features which hasn't been included in this package. Please open an issue if you need a feature and think it will benefit the community.

Goals

This package has multiple goals:

  1. Be an almost identical fork of the great useraccounts package with the difference of being dependent only on react (no blaze/jquery/templating).

  2. Allow an easy migration path for applications which already use the useraccounts package.

  3. Make sense. The codebase should be understandable and easy to modify.

  4. Stay actively maintained.

Setup

Important - Please note that you must provide a set of components either by using one of the versions below or by adding your own

Also note that it's mandatory to call AccountsReact.configure on both client/server even with an empty object!

Styled versions

Pick the package that suit your app. (Create it if it doesn't exist!)

If you've created a package and want to include it here, please open a pull request with a link to the package on atmoshperejs

Routing

This package currently supports react-router.

React Router

If you want to use different paths for your routes see custom routes

import React, { Component } from 'react'
import { Redirect } from 'react-router'
import { Route, Switch } from 'react-router-dom'
import { AccountsReactComponent } from 'meteor/meteoreact:accounts'

class Authentication extends Component {

  render () {
    const arState = this.arState

    return (
      <Switch>
        <Route exact path='/sign-in'          component={arState} />
        <Route exact path='/sign-up'          component={arState} />
        <Route exact path='/forgot-password'  component={arState} />
        <Route exact path='/change-password'  component={arState} />
        <Route exact path='/reset-password/:token' component={arState} />
        <Route exact path='/resend-verification'   component={arState} />
      </Switch>
    )
  }

  arState = ({ match, history }) => {
    const { path, params } = match

    // Cant change password if not logged in.
    if (Meteor.userId() && path !== '/change-password') {
      return (<Redirect to='/' />)
    }

    return (
      <AccountsReactComponent
        history={history}
        route={path}
        token={params.token} // for the reset-password route
      />
    )
  }
}

export default Authentication

States

When you render AccountsReactComponent there are 3 ways to make it render the form you want

  • Pass a "state" prop
  <AccountsReactComponent
    state='signUp'
  />
  • Pass a "route" prop
  <AccountsReactComponent
    route='/sign-up'
  />

You must pass a route that resolves to one of the possible states (example)

  • Configure the "defaultState" key
  AccountsReact.configure({
    defaultState: 'signUp'
  })

Currently available states are:

State Details
changePwd Set a new password (Must be logged in)
forgotPwd Send a password reset email to an address
resetPwd Set a new password (After reset, a "token" prop must be passed to AccountsReactComponent)
signIn Login form
signUp Registration form
resendVerification Resend email with verification link

Configuration

  • Configuration should be the same on both ends. A good place to put the configuration file is imports/both/startup (and import it on both ends)

  • Although it is valid to use different configurations for the client and the server you'd better avoid it in order to prevent possible unknown side-effects. However, it's perfectly fine to set client configurations (like texts) only on the client and vice versa.

  • Configuration must also run before Meteor's startup.

The following is a list with details about each configurable option.

Option Type Default Description
Behaviour
confirmPassword Boolean true Ask the password twice for confirmation (only on sign up)
defaultState String 'signIn' The state to use if no route has been declared (via route or prop)
disableForgotPassword Boolean false Disable the option the call Accounts.forgotPassword
enablePasswordChange Boolean true Make the changePwd state available, you can either set it to false or just don't set a route for it.
focusFirstInput Boolean !Meteor.isCordova Whether to focus the first input when a form is rendered.
forbidClientAccountCreation Boolean false Dont allow user creation on the client. If set to true - no sign up link/form will be available.
lowercaseUsername Boolean false Transform username field to lowercase upon registration
loginAfterSignup Boolean true Login automatically after sign up
overrideLoginErrors Boolean true Show general error on failed login (without specifying which field was wrong)
sendVerificationEmail Boolean true Send email verification after successful registration
setDenyRules Boolean true Apply default deny rules on Meteor.users collection
disableConfigureLoginService Boolean true Disable configureLoginService() insecure method
Appearance
hideSignInLink Boolean false When set to true, asks to never show the link to the sign in page
hideSignUpLink Boolean false When set to true, asks to never show the link to the sign up page
showForgotPasswordLink Boolean false Specifies whether to display a link to the forgot password page/form
showResendVerificationLink Boolean false Specifies whether to display a link to the resend verification page/form
showLabels Boolean true Specifies whether to display text labels above input elements
showPlaceholders Boolean true Specifies whether to display place-holder text inside input elements
Client side validation
continuousValidation Boolean false Validate input as the user type (on every "onChange" event)
negativeValidation Boolean true Validate input on every "onBlur" event (only after first data insertion)
Hooks
onLoginHook Function A function to be called after a successful login
onLogoutHook Function Triggered by calling AccountsReact.logout()
onSubmitHook Function A function to be called after a form submission. It takes 2 arguments (error, state).
preSignupHook Function A function to be called before calling the "createUser" method. It takes 2 arguments (password, info). Password is the raw password (before hashing), info is the object with all the data about the new user (you can modify it directly)
showReCaptcha Boolean false Add reCaptcha mechanism to the sign up form (details)
OAuth
Redirects
mapStateToRoute

Hooks

const onLogoutHook = () => {
  // A good use case will be to redirect the user somewhere
}

const onSubmitHook = (err, state) => {
  if (!err) {
    if (state === 'signIn') {
      //
    }
    if (state === 'signUp') {
      //
    }  
  }
}

const preSignupHook = (password, info) => {
  /*
    info structure might look like this
    {
      username,
      email,
      password (hashed),
      profile
    }
  */
}

AccountsReact.configure({
  onLogoutHook,
  onSubmitHook,
  preSignupHook
})

ReCaptcha

First, obtain the necessary API keys from here

Choose one of the following ways to configure reCaptcha settings (Make sure your secretKey never reach the client!)

  "public": {
    "reCaptcha": {
      "siteKey": SITE KEY,
      // params
    }
  }
  "reCaptcha": {
    "secretKey": SECRET KEY
  }
  AccountsReact.configure({
    showReCaptcha: true
  })
  • Configuration Object
  AccountsReact.configure({
    reCaptcha: {
      // params (except secretKey!)
    },
    showReCaptcha: true
  })

And on a server only file

  AccountsReact.configure({
    reCaptcha: {
      secretKey: SECRET KEY
    }
  })

List of available params (except callback)

OAuth

You can specify whether to allow users to login with 3rd party service. The only requirements are that you add the service-configuration package (meteor add service-configuration) and the relevant packages for the services you want (e.g accounts-google. accounts-facebook, etc...)

And configure the services you want to support (server side)

  // google example
  ServiceConfiguration.configurations.update(
    { service: "google" },
    {
      $set: {
        "loginStyle": "popup",
        "clientId": "-",
        "secret": "-"
      }
    }
  )

You can also specify additional options for each service like so

  AccountsReact.configure({
    oauth: {
      'google': {
        // options
      },
      'facebook': {
        // ...
      }
    }
  })

You can find the available options here

Redirects

You can specify directly what happens when a user clicks on a link that normally will redirect him to a different form (e.g "Forgot your password?" link).

  AccountsReact.configure({
    redirects: {
      toSignUp: () => {},
      toSignIn: () => {},
      toForgotPwd: () => {}
    }
  })

Note that if you set any of the above, its your responsibility to take the user to a different route. If routing support (no internal state) is what you seek, you should probably just pass a "history" prop (see example above) to AccountsReactComponent

Custom Routes

Behind the scenes, AccountsReactComponent will use an object called mapStateToRoute to map different routes to the desired states.

The default object used is the following

  mapStateToRoute: {
   signIn: '/sign-in',
   signUp: '/sign-up',
   forgotPwd: '/forgot-password',
   changePwd: '/change-password',
   resetPwd: '/reset-password',
   resendVerification: '/resend-verification'
  }

You can easily override it with

  AccountsReact.configure({
    mapStateToRoute: {
      signIn: '/login',
      signUp: '/register'
      // ...
    }
  })

Fields

Form fields are defined as objects in an array and can be easily customized to your needs. You can edit, add or remove fields directly or via one of the built in functions (addField, removeField ...)

The supported properties are listed in the following table. Note that you can also specify your own properties

Property Type Required Description
_id String X A unique field's id/name (internal use only) to be also used as attribute name into Meteor.user().profile in case it identifies an additional sign up field. Usually all lowercase letters
type String X Specifies the input element type. At the moment supported inputs are: password, email, text, select, radio
displayName String The field's label text. The text label is shown only if showLabels options is set to true
errStr String Error message to display in case of a false validation.
exclude Boolean (On sign up only) If set to true the field will be excluded from the new user object
func Function Specify a custom function for validation. (example below)
minLength Integer If specified, requires the content of the field to be at least minLength characters
maxLength Integer If specified, require the content of the field to be at most maxLength characters.
options [Object] In case type property is set to "select" or "radio", this field must be set to an array of options to be used
placeholder String The field's (input) placeholder text. The place-holder is shown only if showPlaceholders option is set to true
re RegExp Specify a regular expression to validate against. (example below)
required Boolean If set to true the corresponding field cannot be left blank
autocomplete String <input> autocomplete tag value

The original user accounts package supports several more properties. Pull requests are more then welcome!

You can see each state default fields here

Examples of func and re properties.

func

{
  _id: 'confirmPassword',
  displayName: 'Confirm password',
  type: 'password',
  placeholder: 'Re-enter your password',
  errStr: 'Password doesn\'t match',
  exclude: true,
  func: (fields, fieldObj, value, model, errorsArray) => {
    /*
      fields:      Current form fields array
      fieldObj:    This object
      value:       This field's value
      model:       Current form values object
      errorsArray: Current form errors array
    */

    if (!this.config.confirmPassword) {
      return true
    }

    // check that passwords match
    const { password } = model
    const { _id, errStr } = fieldObj

    if (typeof password === 'string') {
      if (!value || (value !== password)) {
        errorsArray.push({ _id, errStr })
        return
      }
    }

    return true
  }
}

re

{
  _id: 'email',
  displayName: 'Email',
  placeholder: 'Enter your email',
  re: regExp.Email,
  errStr: 'Please enter a valid email'
}

Add Fields

To add additional fields, you must specify the state you want to mutate, and an array of object(s) containing your field's data.

import { AccountsReact } from 'meteor/meteoreact:accounts'

AccountsReact.addFields('signUp', [
  {
    _id: 'fullName',
    displayName: 'Full Name',
    placeholder: 'Enter your full name',
    minLength: 4,
    maxLength: 70,
    required: true,
    errStr: 'This field must contain at least 4 characters and no more than 70',
    autocomplete: 'name'
  }
])

Remove Fields

This functionality is not implemented yet, You can help

Edit Fields

This functionality is not implemented yet, You can help

Texts

Configuring the text to be used by the forms is done via the AccountsReact.configure function.

The default configuration object contains a texts property which you can view here

Here is an example of how to override those

import { AccountsReact } from 'meteor/meteoreact:accounts'
AccountsReact.configure({
  texts: {
    button: {
      changedPwd: 'Change your password!'
    },
    info: {
      emailSent: 'Check your inbox!'
    },
    loginForbiddenMessage: 'The username or password is incorrect'
  }
})

Override Styling

Lets say that you are using semantic-ui-react and meteoreact:accounts-semantic and want to add a simple description below each input field.

Instead of copying the full package into your local packages folder and directly change the code (which is totally legitimate and will work just fine!) you can look at the source code of that package and copy only the implementation of the input field into your project.

From there you can edit (almost*) anything you'd like. Save the file when you are done and then add it like so:

import { AccountsReact } from 'meteor/meteoreact:accounts'
import YourInputField from '...'

AccountsReact.style({
    InputField: YourInputField
})

You can override any of the following fields

InputField, SelectField, RadioField, SubmitField, TitleField, ErrorsField

*Dont edit or remove anything that might break the core functionality (like the onChange handlers for example)

Contributing

  1. Fork this repo

  2. git clone https://github.com/royGil/accounts-react-demo && cd accounts-react-demo

  3. git clone https://github.com/{your_account}/accounts-react packages/meteoreact:accounts

  4. meteor npm install

From this point you can make changes to the package folder and run the demo app to see them.

Note that if you want to test anything related to the social buttons you'll have to include a proper settings.json file (see example below)

To commit your changes

  1. cd packages/meteoreact:accounts

  2. npm install (so you can run tests)

  3. npm test and make sure there are no errors

  4. Push your changes (from within the "meteoreact:accounts" folder!) and create a PR from your fork on github.

I'll appreciate if you write tests for your new commit but its not a requirement.


{
  "services": {
    "google": {
      "loginStyle": "popup",
      "clientId": "XXX",
      "secret": "XXX"
    },
    "facebook": {
      "loginStyle": "popup",
      "appId": "XXX",
      "secret": "XXX"
    },
    "github": {
      "loginStyle": "popup",
      "clientId": "XXX",
      "secret": "XXX"
    },
    "twitter": {
      "loginStyle": "popup",
      "consumerKey": "XXX",
      "secret": "XXX"
    }
  }
}