🐶 Account client API for the browser
JavaScript

README.md

hoodie-account-client

Account client API for the browser

Build Status Coverage Status Dependency Status devDependency Status

hoodie-account-client is a JavaScript client for the Account JSON API. It persists session information in localStorage (or your own store API) and provides front-end friendly APIs for things like creating a user account, confirming, resetting a password, changing profile information, or closing the account.

There is also an admin-specific account client

Example

// Account loaded via <script> or require('@hoodie/account-client')
var account = new Account('https://example.com/account/api')

if (account.isSignedIn()) {
  renderWelcome(account)
}

account.on('signout', redirectToHome)

API

Constructor

new Account(options)
Argument Type Description Required
options.url String Path or full URL to root location of the account JSON API Yes
options.id String The initial account id can be passed. Useful for apps that can be used locally without an account. Defaults to random id
options.cacheKey String Name of localStorage key where to persist the session cache. Not used if options.cache is passed. Defaults to account
options.cache Object Object with .get(), .set(properties) and .unset() methods to persist the account status. Each method must return a promise, .get() resolves with the account’s properties or an empty object. Defaults to a localStorage-based API, see also options.cacheKey
options.validate Function Optional function to validate account before sending sign up / sign in / update requests No

Returns account API.

Example

new Account({
  url: '/api',
  id: 'user123',
  cacheKey: 'myapp.session',
  validate: function (options) {
    if (options.username.length < 3) {
      throw new Error('Username must have at least 3 characters')
    }
  }
})

account.ready

Read-only. Promise that resolves once the account instance loaded its current state from the store.

account.id

Read-only. Returns the account id. Cannot be accessed until the account.ready promise resolved.

account.username

Read-only. Returns the username if signed in, otherwise undefined. Cannot be accessed until the account.ready promise resolved.

account.validate

Calls the function passed into the Constructor. Returns a Promise that resolves to true by default

account.validate(options)
Argument Type Required
options.username String No
options.password String No
options.profile Object No

Resolves with an argument.

Rejects with any errors thrown by the function originally passed into the Constructor.

Example

var account = new Account({
  url: '/api',
  cacheKey: 'app.session',
  validate: function (options) {
    if (options.password.length < 8) {
      throw new Error('password should contain at least 8 characters')
    }
  }
})

account.validate({
  username: 'DocsChicken',
  password: 'secret'
})

.then(function () {
  console.log('Successfully validated!')
})

.catch(function (error) {
  console.log(error) // should be an error about the password being too short
})

account.isSignedIn

Returns true if user is currently signed in, otherwise false. Cannot be accessed until the account.ready promise resolved.

account.isSignedIn()

account.hasInvalidSession

Checks account.session.invalid property. Returns true if user has invalid session, otherwise undefined. Cannot be accessed until the account.ready promise resolved.

account.hasInvalidSession()

account.signUp

Creates a new user account on the Hoodie server. Does not sign in the user automatically, account.signIn must be called separately.

account.signUp(accountProperties)
Argument Type Required
accountProperties.username String Yes
accountProperties.password String Yes

Resolves with accountProperties:

{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00.000Z",
  "updatedAt": "2016-01-01T00:00.000Z"
}

Rejects with:

InvalidError Username must be set
SessionError Must sign out first
ConflictError Username <username> already exists
ConnectionError Could not connect to server

Example

account.signUp({
  username: 'pat',
  password: 'secret'
}).then(function (accountProperties) {
  alert('Account created for ' + accountProperties.username)
}).catch(function (error) {
  alert(error)
})

🐕 Implement account.signUp with profile: {...} option: #11


account.signIn

Creates a user session

account.signIn(options)
Argument Type Description Required
options.username String - Yes
options.password String - Yes

Resolves with accountProperties:

{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00.000Z",
  "updatedAt": "2016-01-02T00:00.000Z",
  "profile": {
    "fullname": "Dr. Pat Hook"
  }
}

Rejects with:

UnconfirmedError Account has not been confirmed yet
UnauthorizedError Invalid Credentials
Error A custom error set on the account object, e.g. the account could be blocked due to missing payments
ConnectionError Could not connect to server

Example

account.signIn({
  username: 'pat',
  password: 'secret'
}).then(function (sessionProperties) {
  alert('Ohaj, ' + sessionProperties.account.username)
}).catch(function (error) {
  alert(error)
})

account.signOut

Deletes the user’s session

account.signOut()

Resolves with sessionProperties like account.signin, but without the session id:

{
  "account": {
    "id": "account123",
    "username": "pat",
    "createdAt": "2016-01-01T00:00.000Z",
    "updatedAt": "2016-01-02T00:00.000Z",
    "profile": {
      "fullname": "Dr. Pat Hook"
    }
  }
}

Rejects with:

Error A custom error thrown in a before:signout hook

Example

account.signOut().then(function (sessionProperties) {
  alert('Bye, ' + sessionProperties.account.username)
}).catch(function (error) {
  alert(error)
})

account.destroy

Destroys the account of the currently signed in user.

account.destroy()

Resolves with sessionProperties like account.signin, but without the session id:

{
  "account": {
    "id": "account123",
    "username": "pat",
    "createdAt": "2016-01-01T00:00.000Z",
    "updatedAt": "2016-01-02T00:00.000Z",
    "profile": {
      "fullname": "Dr. Pat Hook"
    }
  }
}

Rejects with:

Error A custom error thrown in a before:destroy hook
ConnectionError Could not connect to server

Example

account.destroy().then(function (sessionProperties) {
  alert('Bye, ' + sessionProperties.account.username)
}).catch(function (error) {
  alert(error)
})

account.get

Returns account properties from local cache. Cannot be accessed until the account.ready promise resolved.

account.get(properties)
Argument Type Description Required
properties String or Array of strings When String, only this property gets returned. If array of strings, only passed properties get returned No

Returns object with account properties, or undefined if not signed in.

Examples

var properties = account.get()
alert('You signed up at ' + properties.createdAt)
var createdAt = account.get('createdAt')
alert('You signed up at ' + createdAt)
var properties = account.get(['createdAt', 'updatedAt'])
alert('You signed up at ' + properties.createdAt)

account.fetch

Fetches account properties from server.

account.fetch(properties)
Argument Type Description Required
properties String or Array of strings When String, only this property gets returned. If array of strings, only passed properties get returned. Property names can have `.` separators to return nested properties. No

Resolves with accountProperties:

{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00.000Z",
  "updatedAt": "2016-01-02T00:00.000Z"
}

Rejects with:

UnauthenticatedError Session is invalid
ConnectionError Could not connect to server

Examples

account.fetch().then(function (properties) {
  alert('You signed up at ' + properties.createdAt)
})
account.fetch('createdAt').then(function (createdAt) {
  alert('You signed up at ' + createdAt)
})
account.fetch(['createdAt', 'updatedAt']).then(function (properties) {
  alert('You signed up at ' + properties.createdAt)
})

account.update

Update account properties on server and local cache

account.update(changedProperties)
Argument Type Description Required
changedProperties Object Object of properties & values that changed. Other properties remain unchanged. No

Resolves with accountProperties:

{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00.000Z",
  "updatedAt": "2016-01-01T00:00.000Z"
}

Rejects with:

UnauthenticatedError Session is invalid
InvalidError Custom validation error
ConflictError Username <username> already exists
ConnectionError Could not connect to server

Example

account.update({username: 'treetrunks'}).then(function (properties) {
  alert('You are now known as ' + properties.username)
})

account.profile.get

Returns profile properties from local cache. Cannot be accessed until the account.ready promise resolved.

account.profile.get(properties)
Argument Type Description Required
properties String or Array of strings When String, only this property gets returned. If array of strings, only passed properties get returned. Property names can have `.` separators to return nested properties. No

Returns object with profile properties, falls back to empty object {}. Returns undefined if not signed in.

Examples

var properties = account.profile.get()
alert('Hey there ' + properties.fullname)
var fullname = account.profile.get('fullname')
alert('Hey there ' + fullname)
var properties = account.profile.get(['fullname', 'address.city'])
alert('Hey there ' + properties.fullname + '. How is ' + properties.address.city + '?')

account.profile.fetch

Fetches profile properties from server.

account.profile.fetch(options)
Argument Type Description Required
properties String or Array of strings When String, only this property gets returned. If array of strings, only passed properties get returned. Property names can have `.` separators to return nested properties. No

Resolves with profileProperties:

{
  "id": "account123-profile",
  "fullname": "Dr Pat Hook",
  "address": {
    "city": "Berlin",
    "street": "Adalberststraße 4a"
  }
}

Rejects with:

UnauthenticatedError Session is invalid
ConnectionError Could not connect to server

Examples

account.fetch().then(function (properties) {
  alert('Hey there ' + properties.fullname)
})
account.fetch('fullname').then(function (fullname) {
  alert('Hey there ' + fullname)
})
account.fetch(['fullname', 'address.city']).then(function (properties) {
  alert('Hey there ' + properties.fullname + '. How is ' + properties.address.city + '?')
})

account.profile.update

Update profile properties on server and local cache

account.profile.update(changedProperties)
Argument Type Description Required
changedProperties Object Object of properties & values that changed. Other properties remain unchanged. No

Resolves with profileProperties:

{
  "id": "account123-profile",
  "fullname": "Dr Pat Hook",
  "address": {
    "city": "Berlin",
    "street": "Adalberststraße 4a"
  }
}

Rejects with:

UnauthenticatedError Session is invalid
InvalidError Custom validation error
ConnectionError Could not connect to server

Example

account.profile.update({fullname: 'Prof Pat Hook'}).then(function (properties) {
  alert('Congratulations, ' + properties.fullname)
})

account.request

Sends a custom request to the server, for things like password resets, account upgrades, etc.

account.request(properties)
Argument Type Description Required
properties.type String Name of the request type, e.g. "passwordreset" Yes
properties Object Additional properties for the request No

Resolves with requestProperties:

{
  "id": "request123",
  "type": "passwordreset",
  "contact": "pat@example.com",
  "createdAt": "2016-01-01T00:00.000Z",
  "updatedAt": "2016-01-01T00:00.000Z"
}

Rejects with:

ConnectionError Could not connect to server
NotFoundError Handler missing for "passwordreset"
InvalidError Custom validation error

Example

account.request({type: 'passwordreset', contact: 'pat@example.com'}).then(function (properties) {
  alert('A password reset link was sent to ' + properties.contact)
})

account.on

account.on(event, handler)

Example

account.on('signin', function (accountProperties) {
  alert('Hello there, ' + accountProperties.username)
})

account.one

Call function once at given account event.

account.one(event, handler)

Example

account.one('signin', function (accountProperties) {
  alert('Hello there, ' + accountProperties.username)
})

account.off

Removes event handler that has been added before

account.off(event, handler)

Example

account.off('singin', showNotification)

Events

Event Description Arguments
signup New user account created successfully accountProperties with .session property
signin Successfully signed in to an account accountProperties with .session property
signout Successfully signed out accountProperties with .session property
passwordreset Email with password reset token sent
unauthenticate Server responded with "unauthenticated" when checking session
reauthenticate Successfully signed in with the same username (useful when session has expired) accountProperties with .session property
update Successfully updated an account's properties accountProperties with .session property

Hooks

// clear user’s local store signin and after signout
account.hook.before('signin', function (options) {
  return localUserStore.clear()
})
account.hook.after('signout', function (options) {
  return localUserStore.clear()
})
Hook Arguments
signin options as they were passed into account.signIn(options)
signout {}

See before-after-hook for more information.

Requests

Hoodie comes with a list of built-in account requests, which can be disabled, overwritten or extended in hoodie-account-server

When a request succeeds, an event with the same name as the request type gets emitted. For example, account.request({type: 'passwordreset', contact: 'pat@example.com') triggers a passwordreset event, with the requestProperties passed as argument.

passwordreset Request a password reset token

Testing

Local setup

git clone https://github.com/hoodiehq/hoodie-account-client.git
cd hoodie-account-client
npm install

In Node.js

Run all tests and validate JavaScript Code Style using standard

npm test

To run only the tests

npm run test:node

To test hoodie-account-client in a browser you can link it into hoodie-account, which provides a dev-server:

git clone https://github.com/hoodiehq/hoodie-account.git
cd hoodie-account
npm install
npm link /path/to/hoodie-account-client
npm start

hoodie-account bundles hoodie-account-client on npm start, so you need to restart hoodie-account to see your changes.

Contributing

Have a look at the Hoodie project's contribution guidelines. If you want to hang out you can join our Hoodie Community Chat.

License

Apache 2.0