@hoodie/account-server-api

Account JavaScript API backed by PouchDB

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
@hoodie/account-server-api
3123.8.74 years ago6 years agoMinified + gzip package size for @hoodie/account-server-api in KB

Readme

hoodie-account-server-api
Account JavaScript API backed by PouchDB

Build Status Coverage Status Dependency Status devDependency Status
@hoodie/account-server-api is a JavaScript API to manage user accounts and authentication backed by PouchDB. Features include account profiles and tokens.

Example

var AccountApi = require('@hoodie/account-server-api')
var PouchDB = require('pouchdb')

var api = new AccountApi({
  PouchDB: PouchDB,
  usersDb: 'my-users-db',
  secret: 'secret123'
})

API

@hoodie/account-server-api is a subset of hoodie-account-client/admin. If you see any inconsistencies, please create an issue

Constructor

new AccountApi(options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>options.PouchDB</code></th>
<td>Object</td>
<td>
  <a href="https://pouchdb.com/">PouchDB</a> constructor
</td>
<td>Yes</td>
<th align="left"><code>options.secret</code></th>
<td>String</td>
<td>
  Server secret, like CouchDB’s <code>couch_httpd_auth.secret</code>
</td>
<td>Yes</td>
<th align="left"><code>options.usersDb</code></th>
<td>String</td>
<td>
  Defaults to <code>\_users</code>
</td>
<td>No</td>

Returns an api instance.
Examples
var PouchDB = require('pouchdb')
var api = new AccountApi({
  PouchDB: PouchDB,
  secret: 'secret123',
  usersDb: 'my-users-db'
})

api.sessions.add()

Admins can create a session for any user.
admin.sessions.add(options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>options.account.username</code></th>
<td>String</td>
<td>Token gets invalidated after first usage</td>
<td>Yes (unless <code>options.account.token</code> set)</td>
<th align="left"><code>options.account.token</code></th>
<td>String</td>
<td>-</td>
<td>Yes (unless <code>options.account.username</code> set)</td>
<th align="left"><code>options.account.password</code></th>
<td>String</td>
<td>
  Only applicable if <code>options.account.username</code> is set.
  If only username is passed then it’s assumed that an admin wants to
  create a session without any validation of user credentials.
</td>
<td>No</td>
<th align="left"><code>options.timeout</code></th>
<td>Number</td>
<td>
  Time from now until expiration of session in seconds. Defaults to no timeout.
</td>
<td>No</td>

Resolves with sessionProperties
{
  id: 'session123',
  // account is always included
  account: {
    id: 'account456',
    username: 'pat@example.com'
  }
}

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>UnconfirmedError</code></th>
<td>Account has not been confirmed yet</td>
<th align="left"><code>NotFoundError</code></th>
<td>Account could not be found</td>
<th align="left"><code>Error</code></th>
<td><em>A custom error set on the account object, e.g. the account could be blocked due to missing payments</em></td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Examples
// create session if pat’s password is "secret"
admin.sessions.add({
  account: {
    username: 'pat',
    password: 'secret'
  }
}).then(function (sessionProperties) {
  var sessionId = sessionProperties.id
  var username = sessionProperties.account.username
}).catch(function (error) {
  console.error(error)
})
// create session for pat
admin.sessions.add({
  account: {
    username: 'pat'
  }
}).then(function (sessionProperties) {
  var sessionId = sessionProperties.id
  var username = sessionProperties.account.username
}).catch(function (error) {
  console.error(error)
})
// create session using a one-time auth token
admin.sessions.add({
  account: {
    token: 'secrettoken123'
  }
}).then(function (sessionProperties) {
  var sessionId = sessionProperties.id
  var username = sessionProperties.account.username
}).catch(function (error) {
  console.error(error)
})

api.sessions.find()

admin.sessions.find(sessionId)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>sessionId</code></th>
<td>String</td>
<td>-</td>
<td>Yes</td>

Resolves with sessionProperties
{
  id: 'session123',
  // account is always included
  account: {
    id: 'account456',
    username: 'pat@example.com'
    // admin accounts have no profile
  }
}

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>NotFoundError</code></th>
<td>Session could not be found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.sessions.find('abc4567').then(function (sessionProperties) {
  console.log('Session is valid.')
}).catch(function (error) {
  if (error.name === 'NotFoundError') {
    console.log('Session is invalid')
    return
  }

  console.error(error)
})

api.sessions.findAll()

---
🐕 TO BE DONE: #27
admin.sessions.findAll(options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>options.include</code></th>
<td>String</td>
<td>
  If set to <code>"account.profile"</code>, the <code>profile: {...}</code>
  property will be added to the response.
</td>
<td>No</td>
<th align="left"><code>options.sort</code></th>
<td>String or String[]</td>
<td>
  string of comma-separated list of attributes to sort by, or array of strings, see
  <a href="http://jsonapi.org/format/#fetching-sorting">JSON API: Sorting</a>
</td>
<td>No</td>
<th align="left"><code>options.fields</code></th>
<td>Object</td>
<td>
  Map of fields to include in response by type, see
  <a href="http://jsonapi.org/format/#fetching-sparse-fieldsets">JSON API: Sparse Fieldset</a>
</td>
<td>No</td>
<th align="left"><code>options.page.offset</code></th>
<td>Number</td>
<td>
  see <a href="http://jsonapi.org/format/#fetching-pagination">JSON API: Pagination</a>
</td>
<td>No</td>
<th align="left"><code>options.page.limit</code></th>
<td>Number</td>
<td>
  see <a href="http://jsonapi.org/format/#fetching-pagination">JSON API: Pagination</a>
</td>
<td>No</td>

Resolves with Array of sessionProperties
[{
  id: 'session123',
  account: {
    id: 'account456',
    username: 'pat@example.com'
  }
}, {
  id: 'session456',
  account: {
    id: 'account789',
    username: 'sam@example.com'
  }
}]

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.sessions.findAll()
  .then(function (sessions) {})
  .catch(function (error) {
    console.error(error)
  })

api.sessions.remove()

admin.sessions.remove(sessionId)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>sessionId</code></th>
<td>String</td>
<td>-</td>
<td>Yes</td>

Resolves with sessionProperties
{
  id: 'session123',
  account: {
    id: 'account456',
    username: 'pat@example.com'
  }
}

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>NotFoundError</code></th>
<td>Session could not be found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.sessions.remove('abc4567')
  .then(function (sessionProperties) {})
  .catch(function (error) {
    console.error(error)
  })

NOTE: #27 Deleting a Session does not really have an effect today, as no session state is kept, and sessions are hash based

api.sessions.removeAll()

---
🐕 TO BE DONE: #27
admin.sessions.removeAll(options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>options.sort</code></th>
<td>String or String[]</td>
<td>
  string of comma-separated list of attributes to sort by, or array of strings, see
  <a href="http://jsonapi.org/format/#fetching-sorting">JSON API: Sorting</a>
</td>
<td>No</td>
<th align="left"><code>options.fields</code></th>
<td>Object</td>
<td>
  Map of fields to include in response by type, see
  <a href="http://jsonapi.org/format/#fetching-sparse-fieldsets">JSON API: Sparse Fieldset</a>
</td>
<td>No</td>
<th align="left"><code>options.page.offset</code></th>
<td>Number</td>
<td>
  see <a href="http://jsonapi.org/format/#fetching-pagination">JSON API: Pagination</a>
</td>
<td>No</td>
<th align="left"><code>options.page.limit</code></th>
<td>Number</td>
<td>
  see <a href="http://jsonapi.org/format/#fetching-pagination">JSON API: Pagination</a>
</td>
<td>No</td>

Resolves with Array of sessionProperties
[{
  id: 'session123',
  account: {
    id: 'account456',
    username: 'pat@example.com'
  }
}, {
  id: 'session456',
  account: {
    id: 'account789',
    username: 'sam@example.com'
  }
}]

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.sessions.removeAll()
  .then(function (sessions) {})
  .catch(function (error) {
    if (error.name === 'NotFoundError') {
      console.log('Session is invalid')
      return
    }

    console.error(error)
  })

api.accounts.add()

admin.accounts.add(object)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Required</th>
</tr>
<th align="left"><code>accountProperties.username</code></th>
<td>String</td>
<td>Yes</td>
<th align="left"><code>accountProperties.password</code></th>
<td>String</td>
<td>Yes</td>

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

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>InvalidError</code></th>
<td>Username must be set</td>
<th align="left"><code>ConflictError</code></th>
<td>Username <strong><username></strong> already exists</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.accounts.add({
  username: 'pat',
  password: 'secret',
  profile: {
    fullname: 'Dr Pat Hook'
  }
})
  .then(function (accountProperties) {})
  .catch(function (error) {
    console.error(error)
  })

api.accounts.find()

An account can be looked up by account.id, username or token.
  • If a username property is present, it will be looked up by username
  • If an id property is present, it will be looked up by accountId
  • If an token property is present, it will be looked up by token

admin.accounts.find(idOrObject, options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>idOrObject</code></th>
<td>String</td>
<td>account ID. Same as <code>{id: accountId}</code></td>
<td>No</td>
<th align="left"><code>idOrObject.id</code></th>
<td>String</td>
<td>account ID. Same as passing <code>accountId</code> as string</td>
<td>No</td>
<th align="left"><code>idOrObject.username</code></th>
<td>String</td>
<td>Lookup account by username</td>
<td>No</td>
<th align="left"><code>idOrObject.token</code></th>
<td>String</td>
<td>Lookup account by one-time token</td>
<td>No</td>
<th align="left"><code>options.include</code></th>
<td>String</td>
<td>
  If set to <code>"profile"</code>, the <code>profile: {...}</code>
  property will be added to the response
</td>
<td>No</td>

Resolves with accountProperties:
{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00:00.000Z",
  "updatedAt": "2016-01-01T00:00:00.000Z",
  // if options.include === 'profile'
  "profile": {
    "fullname": "Dr. Pat Hook"
  }
}

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>NotFoundError</code></th>
<td>Account not found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.accounts.find({ username: 'pat' })
  .then(function (accountProperties) {})
  .catch(function (error) {
    console.error(error)
  })

api.accounts.findAll()

admin.accounts.findAll(options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>options.include</code></th>
<td>String</td>
<td>
  If set to <code>"profile"</code>, the <code>profile: {...}</code>
  property will be added to the response.
</td>
<td>No</td>
<th align="left"><code>options.sort</code></th>
<td>String or String[]</td>
<td>
  string of comma-separated list of attributes to sort by, or array of strings, see
  <a href="http://jsonapi.org/format/#fetching-sorting">JSON API: Sorting</a>
</td>
<td>No</td>
<th align="left"><code>options.fields</code></th>
<td>Object</td>
<td>
  Map of fields to include in response by type, see
  <a href="http://jsonapi.org/format/#fetching-sparse-fieldsets">JSON API: Sparse Fieldset</a>
</td>
<td>No</td>
<th align="left"><code>options.page.offset</code></th>
<td>Number</td>
<td>
  see <a href="http://jsonapi.org/format/#fetching-pagination">JSON API: Pagination</a>
</td>
<td>No</td>
<th align="left"><code>options.page.limit</code></th>
<td>Number</td>
<td>
  see <a href="http://jsonapi.org/format/#fetching-pagination">JSON API: Pagination</a>
</td>
<td>No</td>

Resolves with Array of accountProperties
[{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00:00.000Z",
  "updatedAt": "2016-01-01T00:00:00.000Z",
  // if options.include === 'profile'
  "profile": {
    "fullname": "Dr. Pat Hook"
  }
}, {
  "id": "account456",
  "username": "sam",
  "createdAt": "2016-01-01T00:00:00.000Z",
  "updatedAt": "2016-01-01T00:00:00.000Z",
  // if options.include === 'profile'
  "profile": {
    "fullname": "Lady Samident"
  }
}]

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.accounts.findAll()
  .then(function (accounts) {})
  .catch(function (error) {
    console.error(error)
  })

api.accounts.update()

An account can be looked up by account.id, username or token.
  • If a username property is present, it will be looked up by username
  • If an id property is present, it will be looked up by accountId
  • If an token property is present, it will be looked up by token

admin.accounts.update(idOrObject, changedProperties, options)
// or
admin.accounts.update(accountProperties, options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>idOrObject</code></th>
<td>String</td>
<td>account ID. Same as <code>{id: accountId}</code></td>
<td>No</td>
<th align="left"><code>idOrObject.id</code></th>
<td>String</td>
<td>account ID. Same as passing <code>accountId</code> as string</td>
<td>No</td>
<th align="left"><code>idOrObject.username</code></th>
<td>String</td>
<td>Lookup account by username</td>
<td>No</td>
<th align="left"><code>idOrObject.token</code></th>
<td>String</td>
<td>Lookup account by one-time token. Token gets invalidated after first usage</td>
<td>No</td>
<th align="left"><code>changedProperties</code></th>
<td>Object</td>
<td>
  Object of properties & values that changed.
  Other properties remain unchanged.
</td>
<td>Yes</td>
<th align="left"><code>accountProperties</code></th>
<td>Object</td>
<td>
  Must have an <code>id</code> or a <code>username</code> property.
  The user’s account will be updated with the passed properties. Existing
  properties not passed remain unchanged.
</td>
<td>Yes</td>
<th align="left"><code>options.include</code></th>
<td>String</td>
<td>
  If set to <code>"profile"</code>, the <code>profile: {...}</code>
  property will be added to the response. Defaults to <code>"profile"</code>
  if <code>accountProperties.profile</code> or <code>changedProperties.profile</code>
  is set.
</td>
<td>No</td>

Resolves with accountProperties:
{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00:00.000Z",
  "updatedAt": "2016-01-01T00:00:00.000Z",
  // if options.include === 'profile'
  "profile": {
    "fullname": "Dr. Pat Hook"
  }
}

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>NotFoundError</code></th>
<td>Account not found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Examples
admin.accounts.update({ username: 'pat' }, { foo: 'bar' })
  .then(function (accountProperties) {})
  .catch(function (error) {
    console.error(error)
  })
// same as
admin.accounts.update({ username: 'pat', foo: 'bar' })
  .then(function (accountProperties) {})
  .catch(function (error) {
    console.error(error)
  })

api.accounts.updateAll()

---
🐕 TO BE DONE: create issue and link it here

api.accounts.remove()

An account can be looked up by account.id, username or token.
  • If a username property is present, it will be looked up by username
  • If an id property is present, it will be looked up by accountId
  • If an token property is present, it will be looked up by token

admin.accounts.remove(idOrObject, changedProperties, options)
// or
admin.accounts.remove(accountProperties, options)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left"><code>idOrObject</code></th>
<td>String</td>
<td>account ID. Same as <code>{id: accountId}</code></td>
<td>No</td>
<th align="left"><code>idOrObject.id</code></th>
<td>String</td>
<td>account ID. Same as passing <code>accountId</code> as string</td>
<td>No</td>
<th align="left"><code>idOrObject.username</code></th>
<td>String</td>
<td>Lookup account by username</td>
<td>No</td>
<th align="left"><code>idOrObject.token</code></th>
<td>String</td>
<td>Lookup account by one-time token</td>
<td>No</td>
<th align="left"><code>changedProperties</code></th>
<td>Object</td>
<td>
  Object of properties & values that changed.
  Other properties remain unchanged.
</td>
<td>Yes</td>
<th align="left"><code>accountProperties</code></th>
<td>Object</td>
<td>
  Must have an <code>id</code> or a <code>username</code> property.
  The user’s account will be updated with the passed properties. Existing
  properties not passed remain unchanged. Note that
  <code>accountProperties.token</code> is not allowed, as it’s not a valid
  account property, but an option to look up an account. An account can
  have multiple tokens at once.
</td>
<td>Yes</td>
<th align="left"><code>options.include</code></th>
<td>String</td>
<td>
  If set to <code>"profile"</code>, the <code>profile: {...}</code>
  property will be added to the response. Defaults to <code>"profile"</code>
  if <code>accountProperties.profile</code> or <code>changedProperties.profile</code>
  is set.
</td>
<td>No</td>

Resolves with accountProperties:
{
  "id": "account123",
  "username": "pat",
  "createdAt": "2016-01-01T00:00:00.000Z",
  "updatedAt": "2016-02-01T00:00:00.000Z",
  "deletedAt": "2016-03-01T00:00:00.000Z",
  // if options.include === 'profile'
  "profile": {
    "fullname": "Dr. Pat Hook"
  }
}

Rejects with:
<th align="left"><code>UnauthenticatedError</code></th>
<td>Session is invalid</td>
<th align="left"><code>NotFoundError</code></th>
<td>Account not found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Examples
admin.accounts.remove({ username: 'pat' }, { reason: 'foo bar' })
  .then(function (accountProperties) {})
  .catch(function (error) {
    console.error(error)
  })
// same as
admin.accounts.remove({ username: 'pat', reason: 'foo bar' })
  .then(function (accountProperties) {})
  .catch(function (error) {
    console.error(error)
  })

api.accounts.removeAll()

---
🐕 TO BE DONE: create issue and link it here

api.requests.add()

---
🐕 TO BE DONE: create issue and link it here
admin.requests.add({
  type: 'passwordreset',
  email: 'pat@example.com'
})

Resolves with
{
  id: 'request123',
  type: 'passwordreset',
  email: 'pat@example.com'
}

api.requests.find()

---
🐕 TO BE DONE: create issue and link it here
admin.requests.find('token123')
admin.requests.find({id: 'token123'})

api.requests.findAll()

---
🐕 TO BE DONE: create issue and link it here
admin.requests.findAll()

api.requests.remove()

---
🐕 TO BE DONE: create issue and link it here
admin.requests.remove('token123')
admin.requests.find({id: 'token123'})

api.requests.removeAll()

---
🐕 TO BE DONE: create issue and link it here

api.account()

The admin.account method returns a scoped API for one account, see below
var account = admin.account(idOrObject)

Examples
admin.account('account123')
admin.account({id: 'account123'})
admin.account({username: 'pat@example.com'})
admin.account({token: 'token456'})

api.account().profile.find()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).profile.find()

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

api.account().profile.update()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).profile.update(changedProperties)

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

api.account().tokens.add()

admin.account('account123').tokens.add(properties)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left">
  <code>properties.type</code>
</th>
<td>String</td>
<td>Every token needs a type, for example <code>"passwordreset"</code></td>
<td>Yes</td>
<th align="left">
  <code>properties.id</code>
</th>
<td>String</td>
<td>Optional token id. If none is passed, a <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">UUID</a> will be generated</td>
<td>No</td>
<th align="left">
  <code>properties.timeout</code>
</th>
<td>Number</td>
<td>Time from now until expiration of token in seconds. Defaults to <code>7200</code> (2 hours)</td>
<td>No</td>

resolves with tokenProperties
{
  "id": "token123",
  "type": "passwordreset",
  "accountId": "account123",
  "contact": "pat@example.com",
  "createdAt": "2016-01-01T00:00:00.000Z"
}

Rejects with:
<th align="left"><code>NotFoundError</code></th>
<td>Account not found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.account({username: 'pat@example.com'}).account.tokens.add({
  type: 'passwordreset',
  email: 'pat@example.com'
})

api.account().tokens.find()

admin.account(idOrObject).tokens.find(id)

<tr>
  <th>Argument</th>
  <th>Type</th>
  <th>Description</th>
  <th>Required</th>
</tr>
<th align="left">
  <code>id</code>
</th>
<td>String</td>
<td>token id</td>
<td>Yes</td>

resolves with tokenProperties
{
  "id": "token123",
  "type": "passwordreset",
  "accountId": "account123",
  "contact": "pat@example.com",
  "createdAt": "2016-01-01T00:00:00.000Z"
}

Rejects with:
<th align="left"><code>NotFoundError</code></th>
<td>Account not found</td>
<th align="left"><code>ConnectionError</code></th>
<td>Could not connect to server</td>

Example
admin.account({username: 'pat'}).tokens.find('token123')

api.account().tokens.findAll()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).tokens.findAll(options)

resolves with array of tokenProperties
[{
  "id": "token123",
  "type": "passwordreset",
  "accountId": "account123",
  "contact": "pat@example.com",
  "createdAt": "2016-01-01T00:00:00.000Z"
}, {
  "id": "token456",
  "type": "session",
  "accountId": "account123",
  "createdAt": "2016-01-02T00:00:00.000Z"
}]

Example
admin.account({username: 'pat'}).tokens.findAll()
  .then(function (tokens) {})
  .catch(function (error) {
    console.error(error)
  })

api.account().tokens.remove()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).tokens.remove(idOrObject)

resolves with tokenProperties
{
  "id": "token123",
  "type": "passwordreset",
  "accountId": "account123",
  "contact": "pat@example.com",
  "createdAt": "2016-01-01T00:00:00.000Z"
}

Example
admin.account({username: 'pat'}).tokens.removes('token123')

api.account().roles.add()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).roles.add(name)

resolves with roleName
"mycustomrole"

Example
admin.account({username: 'pat'}).roles.add('mycustomrole')

api.account().roles.findAll()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).roles.add(name)

resolves with array of roleNames
["mycustomrole", "myothercustomrole"]

Example
admin.account({username: 'pat'}).roles.findAll()
  .then(function (roles) {})
  .catch(function (error) {
    console.error(error)
  })

api.account().roles.remove()

---
🐕 TO BE DONE: create issue and link it here
admin.account(idOrObject).roles.remove(name)

resolves with roleName
"mycustomrole"

Example
admin.account({username: 'pat'}).roles.remove('mycustomrole')

Events

---
🐕 TO BE DONE: #35
Events emitted on
  • admin.sessions
  • admin.accounts
  • admin.requests

<th align="left"><code>change</code></th>
<td>
  triggered for any <code>add</code>, <code>update</code> and <code>remove</code> event
</td>
<th align="left" colspan="2"><code>add</code></th>
<th align="left" colspan="2"><code>update</code></th>
<th align="left" colspan="2"><code>remove</code></th>

admin.sessions.on('change', function (eventName, session) {})
admin.accounts.on('update', function (account) {})
admin.requests.on('remove', handler)

Contributing

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

Testing

Local setup
git clone https://github.com/hoodiehq/hoodie-account-server-api.git
cd hoodie-account-server-api
npm install

Run all tests and code style checks
npm test

If you want to run a single test you can do it with
./node_modules/.bin/tap test/unit/sessions/remove-test.js

License

Apache 2.0