auth/model/Oauth2BearerToken.js

/**
 * @module flitter-auth/model/Oauth2BearerToken
 */

const Model = require('flitter-orm/src/model/Model')

/**
 * Flitter model spec for an Oauth2BearerToken which is compatible
 * with the node-oauth2-server package and can be used as authentication
 * for internal or external APIs.
 * @extends module:flitter-orm/src/model/Model~Model
 */
class Oauth2BearerToken extends Model {
    /**
     * Defines the services required by this model.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'models', 'auth']
    }

    /**
     * Defines the schema for this model.
     * @returns {object}
     */
    static get schema() {
        return {
            accessToken: String,
            clientID: String,
            expires: Date,
            userID: String,
        }
    }

    /**
     * Get an instance of the {module:flitter-auth/model/Oauth2Client~Oauth2Client} model with
     * the corresponding client id and secret.
     * @param {string} clientID - the client ID
     * @param {string} clientSecret - the client secret
     * @param {function} callback - passed boolean for error as the first param and the client or null for the second
     */
    static getClient(clientID, clientSecret, callback) {
        const Client = this.prototype.models.get('auth::Oauth2Client')
        Client.findOne({clientID, clientSecret}).then(client => {
            if ( !client ) callback(false, null)
            else callback(false, client)
        })
    }

    /**
     * Determines if the client with the specified ID is allowed to use
     * the specified grant type.
     * @param {string} clientID - the client ID
     * @param {string} grantType - the grant type
     * @param {function} callback - passed boolean for error and boolean for whether the client can use the grant type
     */
    static grantTypeAllowed(clientID, grantType, callback) {
        const Client = this.prototype.models.get('auth::Oauth2Client')
        Client.findOne({clientID}).then(client => {
            if ( !client ) callback(false, false)
            else callback(false, client.can(grantType))
        })
    }

    /**
     * Attempts to authenticate a user by username and password. If that
     * succeeds, fetches the {module:flitter-auth/model/User~User} instance.
     * @param {string} username - the username
     * @param {string} password - the attempted password
     * @param {function} callback - passed boolean for error, and null or instance of the User on success
     */
    static getUser(username, password, callback) {
        // defaults to the default auth provider
        const provider = this.prototype.auth.get_provider()
        provider.get_login_args({username, password}).then(args => {
            provider.login(...args).then(result => {
                if ( !result ) callback(false, null)
                else callback(false, result)
            })
        })
    }

    /**
     * Save a new instance of this access token with the given parameters.
     * @param {string} accessToken - the access token
     * @param {string} clientID - the associated client ID
     * @param {Date} expires - the datetime when the token expires
     * @param {module:flitter-auth/model/User~User} user - the associated User instance
     * @param {function} callback - passed null on success, or an error
     */
    static saveAccessToken(accessToken, clientID, expires, user, callback) {
        const token = new this({
            accessToken,
            clientID,
            expires,
            userID: user.id,
        })

        token.save().then(() => callback(null)).catch((error) => callback(error))
    }

    /**
     * Given the access token, retrieve the information about that token
     * in the format supported by node-oauth2-server.
     * @param {string} bearerToken - the bearer token
     * @param {function} callback - passed boolean for error, and null or the result of this.serialize() on success
     */
    static getAccessToken(bearerToken, callback) {
        this.findOne({accessToken: bearerToken})
            .then(token => {
                if ( !token ) callback(false, null)
                else callback(null, token.serialize())
            })
    }

    /**
     * Given a bearer token, generate an instance of {module:flitter-auth/model/Oauth2AuthorizationTicket~Oauth2AuthorizationTicket}
     * @param {string} bearerCode - the bearer token
     * @param {function} callback - passed boolean for error, and null or object of information about the token
     */
    static getAuthCode(bearerCode, callback) {
        const Code = this.prototype.models.get('auth::Oauth2AuthorizationTicket')
        Code.findOne({token: bearerCode, redeemed: false}).then(token => {
            if ( !token ) callback(false, null)
            else {
                token.redeemed = true
                token.save().then(() => {
                    callback(false, {
                        clientId: token.client_id,
                        userId: token.user_id,
                        expires: token.expires,
                    })
                })
            }
        })
    }

    /**
     * Serializes this instance to the format understood by node-oauth2-server.
     * @returns {object}
     */
    serialize() {
        return {
            user: {
                id: this.userID,
            },
            expires: this.expires,
        }
    }
}

module.exports = exports = Oauth2BearerToken