@555platform/author.ts

555 Platform OAuth2 TS SDK

Usage no npm install needed!

<script type="module">
  import 555platformAuthorTs from 'https://cdn.skypack.dev/@555platform/author.ts';
</script>

README

Build

Author.ts

Isomorphic Typescript toolkit for OAuth 2.0 with 555 Platform

Install

From npm

npm install @555platform/Author.ts

Initialize - client

import { WebAuth } from '@555platform/Author.ts';

const webAuth = new WebAuth({
      domain: config.domain,
      clientID: config.clientID,
      responseType: 'code',
      redirectURI: config.redirectURI,
      scope: 'everything',
      authServer: config.authServer
    });

The above is example of typical initialization of client side SDK. authServer field is optional and it will default to 555 Platform production server.

Available response types are code, token, id_token, and id_token token.

Initialize - server

Sample initialization on server side. Note, currently SDK helper functions support only express.js.

serverAuth = ServerAuth({
  domain: config.domain,
  clientID: config.clientID,
  clientSecret: config.clientSecret,
  redirectURI: config.redirectURI,
  authServer: config.authServer
});

Using response type code

When using response type code you will need to initiate login from the client side that will take you to 555 Platform login screen. After successful login 555 Platform will redirect to URI that should be implemented on application server. In order to implement this flow we will need code on both client and server. Let's start with the client code first.

const webAuth = new WebAuth({
      domain: config.domain,
      clientID: config.clientID,
      responseType: 'code',
      redirectURI: config.redirectURI,
      scope: 'everything',
      authServer: config.authServer
    });
    webAuth.login({ state: 'xyz' });

Above code will initialize client side of SDK and initiate login flow with response type code.

Now on the server side we will need to create basic set up with express.js. In the index.js file:

const express = require('express');
const bodyParser = require('body-parser');
const cookieSession = require('cookie-session');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(
  cookieSession({
    maxAge: 30 * 24 * 60 * 60 * 1000,
    keys: ['victor is a cool cat']
  })
);

require('./routes/authRoutes')(app);

const PORT = process.env.PORT || 3033;
app.listen(PORT);

Since we will be using cookie to store 555 Platform access token we also included cookie-session module.

Next we need to create route that will handle redirect from login page. In file routes/authRoutes.js add the following code:

const { ServerAuth } = require('@555platform/Author.ts');
const config = require('./config');

serverAuth = ServerAuth({
  domain: config.domain,
  clientID: config.clientID,
  clientSecret: config.clientSecret,
  redirectURI: config.redirectURI,
  authServer: config.authServer
});

serverAuth.serializeUser(payload => {
  return payload.access_token;
});

serverAuth.deserializeUser(payload => {
  return payload;
});

module.exports = app => {
  app.use(serverAuth.session);

  app.get(
    '/auth/555/callback',
    serverAuth.authenticate({
      successRedirect: config.loginSuccess,
      failureRedirect: config.loginFailure,
      state: 'xyz',
      nonce: '123'
    })
  );
}

The first thing we did is to initialize server side SDK with client ID/secret, redirect URI, domain and optional auth server URL.

Two calls serializeUser and deserializeUser are used to put/retrieve access token from the req.user and req.session.user.

We are also providing session middleware that attaches user object to the request.

Finally, we handle /auth/555/callback URI that received code from 555 Platform. Helper function authenticate takes care of entire exchange of code for access token and it will redirect to either success or failure URLs as specified above.

It is also possible to control what happens after authenticate completes code exchange for access token. If you rather not automatically redirect use the following sample code instead to introduce your own logic:

app.get(
    '/auth/555/callback',
    serverAuth.authenticate({
      state: 'xyz',
      nonce: '123'
    }), (req, res) => {
    console.log('AUTH CHECK: ', req.auth)
    if (req.auth) {
      res.send({message: 'authed'})
      return
    }

    res.status(401).send({message: 'unauthorized'})
  }
);

Note: In order to receive authorization data in req.auth do not pass functions to serializeUser and deserializeUser. If you do req.auth will not contain any data but rather req.user will contain deserialized information you selected.

Using response type implicit

For implicit flow you initiate call to login from the client and receive redirected URL with access token and/or id token directly without the need to exchange the code. This is useful if you want to handle authentication directly in the client.

To initiate login with implicit flow call SDK with the following sample code:

import { WebAuth } from '@555platform/Author.ts';

.
.
.

const webAuth = new WebAuth({
      domain: config.domain,
      clientID: config.clientID,
      responseType: 'id_token token',
      redirectURI: config.implicitRedirectURI,
      scope: 'openid email',
      authServer: config.authServer
    });
    webAuth.login({ state: 'xyz' });

In this case, after login is completed 555 Platform will redirect to URL specified in redirectURI with the following sample values:

/implicit#access_token=<access_token>&expires_in=216000&id_token=<id_token>&scope=openid+email&state=xyz&token_type=Bearer

Identity APIs

Identity APIs allow seearching for users or single user and updating user information.

Query for users

You can perform a query for users that match certain criteria, for example, user_name starts with rob. Search can be paginated by providing skip and limit parameters. Here is an example:

const identity = new Identity();
identity
  .findUsers(
    '<server token>',
    { user_name: 'regex(.*rob.*) options(i)' }
  )
  .then((data: any) => console.log("Found users:", data))
  .catch((error: Error) => console.log("Error finding users", error));

Find single user by userId:

const identity = new Identity();
identity
  .findUser(
    "<server token>",
    "10c8f37c-057a-11ea-a90f-784f4371df5c"
  )
  .then((data: any) => console.log("Found user:", data))
  .catch((error: Error) => console.log("Error finding user", error));

Update user information

Here is an example to update user_name:

const identity = new Identity();
identity
  .updateUser(
    "<server token>",
    "10c8f37c-057a-11ea-a90f-784f4371df5c",
    {
      user_name: "Rob Tester"
    }
  )
  .then((data: any) => console.log("Updated user:", data))
  .catch((error: Error) => console.log("Error updating user", error));

Subdomains

Subdomains are child domains of the domain. They are useful for grouping multiple domains as owned by single application but each domain represents different set of identities. This is useful for implementing concepts like workspaces in Slack for example.

Create Subdomain

const serverAuth = new ServerAuth({
      domain: 'test.domain.com',
      clientID: 'blahblahblah',
      clientSecret: 'blahblahblah123',
      redirectURI: '/auth/iris/callback',
      authServer: 'http://localhost',
      userinfoRoot: '/oauth2'
    });

serverAuth
      .createSubdomain('<application name>', '<application friendly name>')
      .then(data => {
        console.log(data)
      })
      .catch(error => {
        console.log(error);
      });

Find Subdomains

findSubdomains function will return list of domains that match either friendlyName (first parameter) or domain name (second parameter). Each parameter is treated as regex of *value* so it can match multiple entries.

For example,

const serverAuth = new ServerAuth({
      domain: 'test.domain.com',
      clientID: 'blahblahblah',
      clientSecret: 'blahblahblah123',
      redirectURI: '/auth/iris/callback',
      authServer: 'http://localhost',
      userinfoRoot: '/oauth2'
    });

    serverAuth
      .findSubdomains('bl', '')
      .then(data => {
        console.log('Found subdomaines', data)
      })
      .catch(error => {
        console.log(error);
      });

Login with Subdomain

To login with subdomain call buildSubdomainLoginURL to retrieve URL to redirect to. This URL will call Author login for specified subdomain.

serverAuth
      .buildSubdomainLoginURL('<subdomain friendly name>', '<state>', '<nonce>', '<scopes or everything>')
      .then(loginURL => {
        console.log(loginURL)
      })
      .catch(error => {
        console.log(error);
      });

Getting public key for the token's domain

Author SDK provides convenience API to automatically look up public key for the specified token. This is useful when validating signature of the JWT on the server that can receive JWTs from multiple domains.

Example,

import { getPublicKeysForToken } from '@555platform/author.ts';

const jwtPublicKey = await getPublicKeysForToken(
    accessToken,
    config.authServerUrl,
    15, // cache age in seconds, default 30s
  );