Have ideas to improve npm?Join in the discussion! »

    @hoodie/account-server-api

    3.8.7 • Public • Published

    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)
    Argument Type Description Required
    options.PouchDB Object PouchDB constructor Yes
    options.secret String Server secret, like CouchDB’s couch_httpd_auth.secret Yes
    options.usersDb String Defaults to \_users No

    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)
    Argument Type Description Required
    options.account.username String Token gets invalidated after first usage Yes (unless options.account.token set)
    options.account.token String - Yes (unless options.account.username set)
    options.account.password String Only applicable if options.account.username 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. No
    options.timeout Number Time from now until expiration of session in seconds. Defaults to no timeout. No

    Resolves with sessionProperties

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

    Rejects with:

    UnauthenticatedError Session is invalid
    UnconfirmedError Account has not been confirmed yet
    NotFoundError Account could not be found
    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

    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)
    Argument Type Description Required
    sessionId String - Yes

    Resolves with sessionProperties

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

    Rejects with:

    UnauthenticatedError Session is invalid
    NotFoundError Session could not be found
    ConnectionError Could not connect to server

    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)
    Argument Type Description Required
    options.include String If set to "account.profile", the profile: {...} property will be added to the response. No
    options.sort String or String[] string of comma-separated list of attributes to sort by, or array of strings, see JSON API: Sorting No
    options.fields Object Map of fields to include in response by type, see JSON API: Sparse Fieldset No
    options.page.offset Number see JSON API: Pagination No
    options.page.limit Number see JSON API: Pagination No

    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:

    UnauthenticatedError Session is invalid
    ConnectionError Could not connect to server

    Example

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

    api.sessions.remove()

    admin.sessions.remove(sessionId)
    Argument Type Description Required
    sessionId String - Yes

    Resolves with sessionProperties

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

    Rejects with:

    UnauthenticatedError Session is invalid
    NotFoundError Session could not be found
    ConnectionError Could not connect to server

    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)
    Argument Type Description Required
    options.sort String or String[] string of comma-separated list of attributes to sort by, or array of strings, see JSON API: Sorting No
    options.fields Object Map of fields to include in response by type, see JSON API: Sparse Fieldset No
    options.page.offset Number see JSON API: Pagination No
    options.page.limit Number see JSON API: Pagination No

    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:

    UnauthenticatedError Session is invalid
    ConnectionError Could not connect to server

    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)
    Argument Type Required
    accountProperties.username String Yes
    accountProperties.password String Yes

    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:

    UnauthenticatedError Session is invalid
    InvalidError Username must be set
    ConflictError Username <username> already exists
    ConnectionError Could not connect to server

    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)
    Argument Type Description Required
    idOrObject String account ID. Same as {id: accountId} No
    idOrObject.id String account ID. Same as passing accountId as string No
    idOrObject.username String Lookup account by username No
    idOrObject.token String Lookup account by one-time token No
    options.include String If set to "profile", the profile: {...} property will be added to the response No

    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:

    UnauthenticatedError Session is invalid
    NotFoundError Account not found
    ConnectionError Could not connect to server

    Example

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

    api.accounts.findAll()

    admin.accounts.findAll(options)
    Argument Type Description Required
    options.include String If set to "profile", the profile: {...} property will be added to the response. No
    options.sort String or String[] string of comma-separated list of attributes to sort by, or array of strings, see JSON API: Sorting No
    options.fields Object Map of fields to include in response by type, see JSON API: Sparse Fieldset No
    options.page.offset Number see JSON API: Pagination No
    options.page.limit Number see JSON API: Pagination No

    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:

    UnauthenticatedError Session is invalid
    ConnectionError Could not connect to server

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

    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:

    UnauthenticatedError Session is invalid
    NotFoundError Account not found
    ConnectionError Could not connect to server

    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)
    Argument Type Description Required
    idOrObject String account ID. Same as {id: accountId} No
    idOrObject.id String account ID. Same as passing accountId as string No
    idOrObject.username String Lookup account by username No
    idOrObject.token String Lookup account by one-time token No
    changedProperties Object Object of properties & values that changed. Other properties remain unchanged. Yes
    accountProperties Object Must have an id or a username property. The user’s account will be updated with the passed properties. Existing properties not passed remain unchanged. Note that accountProperties.token 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. Yes
    options.include String If set to "profile", the profile: {...} property will be added to the response. Defaults to "profile" if accountProperties.profile or changedProperties.profile is set. No

    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:

    UnauthenticatedError Session is invalid
    NotFoundError Account not found
    ConnectionError Could not connect to server

    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)
    Argument Type Description Required
    properties.type String Every token needs a type, for example "passwordreset" Yes
    properties.id String Optional token id. If none is passed, a UUID will be generated No
    properties.timeout Number Time from now until expiration of token in seconds. Defaults to 7200 (2 hours) No

    resolves with tokenProperties

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

    Rejects with:

    NotFoundError Account not found
    ConnectionError Could not connect to server

    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)
    Argument Type Description Required
    id String token id Yes

    resolves with tokenProperties

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

    Rejects with:

    NotFoundError Account not found
    ConnectionError Could not connect to server

    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
    change triggered for any add, update and remove event
    add
    update
    remove
    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

    Install

    npm i @hoodie/account-server-api

    DownloadsWeekly Downloads

    34

    Version

    3.8.7

    License

    Apache-2.0

    Unpacked Size

    106 kB

    Total Files

    63

    Last publish

    Collaborators

    • avatar