Skip to content

Latest commit

 

History

History
709 lines (497 loc) · 19.5 KB

API.md

File metadata and controls

709 lines (497 loc) · 19.5 KB

Authmosphere API Documentation

Table of contents

  1. TokenCache
  2. OAuth tooling
  3. Express middlewares
  4. Types
  5. Mock tooling

Token Cache

Class to request and cache tokens on client-side.

Usage

import {
  TokenCache,
  OAuthGrantType,
  Token
} from 'authmosphere';

const options = {
  cacheConfig: {
    percentageLeft: 0.75
  },
  logger: someExternalLogger
};

const oAuthConfig = {
  grantType: OAuthGrantType.CLIENT_CREDENTIALS_GRANT,
  accessTokenEndpoint: 'https://example.com/access_token',
  credentialsDir: './credentialsDir'
};

const tokenConfig = {
  'service-foo': ['foo.read', 'foo.write'],
  'service-bar': ['bar.read']
};

// create a new TokenCache instance
const tokenCache = new TokenCache(tokenConfig, oAuthConfig, options);

// request and resolve a token from the cache
tokenCache
  .get('service-foo') // needs to match with a key from 'tokenConfig'
  .then((token: Token) => {
    // ...use the token...
  });

constructor

Toggle description

Signature

constructor(tokenConfig, oauthConfig, options)

Arguments

get

Toggle description

Returns cached token or requests a new one if lifetime (as configured in cacheOptions.cacheConfig) is expired.

Signature

get(tokenName) => Promise<Token>

Arguments

  • tokenName: string - Key of the token as configured in tokenConfig

Returns

Promise<Token> that resolves with a token with configured scopes. In case of error rejects with an error message.

refreshToken

Toggle description

Triggers the request of a new token. Invalidates the old cache entry.

Signature

refreshToken(tokenName: string) => Promise<Token>

Arguments

  • tokenName: string - Key of the token as configured in tokenConfig

Returns

Promise<Token> that resolves with a token with configured scopes. In case of error rejects with an error message.

refreshAllTokens

Toggle description

Triggers the request of a new token for all configured ones. Invalidates all cache entries.

Signature

refreshAllTokens() => Promise<TokenMap>

Returns

Promise<TokenMap> that resolves with a map of tokens with configured scopes. In case of error rejects with an error message.


OAuth Tooling

This tooling provides helper functions to request and validate tokens based on the OAuth 2.0 RFC 6749 specification.

getAccessToken

Toggle description

Requests a token based on the given configuration (which specifies the grant type and corresponding parameters). See the OAuthConfig documentation for details.

Usage

import {
  OAuthGrantType,
  ClientCredentialsGrantConfig,
  Token,
  getAccessToken
} from 'authmosphere';

const config: ClientCredentialsGrantConfig = {
  grantType: OAuthGrantType.CLIENT_CREDENTIALS_GRANT,
  credentialsDir: './crendentials',
  accessTokenEndpoint: 'https://example.com/token_validation',
  scopes: ['my-app.read', 'my-app.write'];
};

getAccessToken(config)
  .then((token: Token) => {
    // ...use the token...
  })
  .catch((err) => {
    // ...handle the error...
  });

Signature

getAccessToken(config[, logger]) => Promise<Token>

Arguments

Returns

Promise<Token> which resolves with the token if the request was successful. Otherwise, rejects with an error message.

getTokenInfo

Toggle description

Requests validation information from the specified tokenInfoUrl and returns a Promise that resolves with these information, if the token is valid. Otherwise, it rejects with an error.

Usage

import {
  Token,
  getTokenInfo
} from 'authmosphere';

getTokenInfo('https://example.com/token_validation', '1234-5678-9000')
  .then((token: Token) => {
    // ...token is valid...
  })
  .catch((err) => {
    // ...token is invalid...
  })

Signature

getTokenInfo<T>(tokenInfoUrl, accessToken[, logger]): Promise<Token<T>>

Arguments

  • tokenInfoUrl: string - OAuth endpoint for validating tokens
  • accessToken: string - access token to be validated
  • logger?: Logger

Returns

Promise<Token> which resolves with the validated token if it is valid. Otherwise, rejects with an error message.

createAuthCodeRequestUri

Toggle description

Helper function to create the URI to request an authorization code when using the Authorization Code Grant.

⚠️ This function only creates the URI, it does not handle the actual request.

Usage

const uri = createAuthCodeRequestUri('https://example.com/authorize', 'http://your-app.com/handle-auth-code', '1234-client-id');

Signature

createAuthCodeRequestUri(authorizationEndpoint, redirectUri, clientId[, queryParams]) => string

Arguments

  • authorizationEndpoint: string - OAuth authorization endpoint
  • redirectUri: string - Absolute URI specifying the endpoint the authorization code is responded to (see OAuth 2.0 specification for details)
  • clientId: string - client id of the requesting application
  • queryParams?: { [index: string]: string } - Set of key-value pairs which will be added as query parameters to the request (for example to add state or scopes)

Returns

string of the created request URI.


Express Tooling

Authmosphere provides two middleware factories to secure Express based http services.

authenticationMiddleware

Toggle description

Middleware that handles OAuth authentication for API endpoints. It extracts and validates the access token from the request.

If configured as a global middleware (see usage section), all requests need to provide a valid token to access the endpoint.
If some endpoints should be excluded from this restriction, they need to be added to the options.publicEndpoints array to be whitelisted.

If validation of the provided token fails the middleware rejects the request with status 401 UNAUTHORIZED.
To overwrite this behavior a custom handler can be specified by passing in options.onNotAuthenticatedHandler (see onNotAuthenticatedHandler).

  • ⚠️  While this middleware could also be configured per endpoint (i.e. app.get(authenticationMiddleware(...), endpoint) it is not recommended as using it as global middleware will force you into a whitelist setup.
    • Make sure authenticationMiddleware is at the top of the registered request handlers. This is essential to guarantee the enforceability of the whitelist strategy.
  • ⚠️  The middleware attaches metadata (scopes of the token) to the express request object. The requireScopesMiddleware relies on this information.

Usage

import {
  authenticationMiddleware
} from 'authmosphere';

app.use(authenticationMiddleware({
  publicEndpoints: ['/heartbeat', '/status'],
  tokenInfoEndpoint: 'https://example.com/token_validation'
});

Signature

authenticationMiddleware(options) => express.RequestHandler

Arguments

requireScopesMiddleware

Toggle description

A factory that returns a middleware that compares scopes attached to express.Request object with a given list (scopes parameter). If all required scopes are matched, the middleware calls next. Otherwise, it rejects the request with 403 FORBIDDEN.

  • ⚠️  This middleware requires scope information to be attached to the Express.request object. The authenticationMiddleware can do this job. Otherwise request.$$tokeninfo.scope: string[] has to be set manually.

There may occur cases where another type of authorization should be used. For that cases options.precedenceFunction has to be set. If the precedence function resolves with anything else than 'true', normal scope validation is applied afterwards.

Detailed middleware authorization flow:

+-----------------------------------+
|   is precedenceFunction defined?  |
+-----------------------------------+
        |             |
        |             | yes
        |             v
        |    +----------------------+  resolve(true)  +--------+       +---------------+
     no |    | precedenceFunction() |---------------->| next() | ----->| call endpoint |
        |    +----------------------+                 +--------+       +---------------+
        |             |
        |             | reject
        v             v
+-----------------------------------+        yes      +--------+       +---------------+
| scopes match with requiredScopes? |---------------->| next() |------>| call endpoint |
+-----------------------------------+                 +--------+       +---------------+
        |
    no/ |
  throw v
+----------------------------------+         yes      +--------------------------------+
| is onAuthorizationFailedHandler  |----------------->| onAuthorizationFailedHandler() |
| configured?                      |                  +--------------------------------+
+----------------------------------+
        |
        |               no                            +--------------------------------+
        +-------------------------------------------->|    response.sendStatus(403)    |
                                                      +--------------------------------+

Usage

import {
  requireScopesMiddleware
} from 'authmosphere';

app.get('/secured/route', requireScopesMiddleware(['scopeA', 'scopeB']), (request, response) => {
  // handle request
});

Signature

(scopes: string[], options?: ScopeMiddlewareOptions) => express.RequestHandler

Arguments


Logging

Toggle description

Logging is an essential part of Authmosphere's tooling. Authmosphere does not rely on console or any other specific logger library, instead every function expects a (optional) reference to an external logger. The logger must fulfill this interface:

interface Logger {
  info(message: string, error?: any): void;
  debug(message: string, error?: any): void;
  error(message: string, error?: any): void;
  fatal(message: string, error?: any): void;
  trace(message: string, error?: any): void;
  warn(message: string, error?: any): void;
}

Types

Toggle description

OAuthConfig

type OAuthConfig =
  ClientCredentialsGrantConfig   |
  AuthorizationCodeGrantConfig   |
  PasswordCredentialsGrantConfig |
  RefreshGrantConfig;
Toggle description
type ClientCredentialsGrantConfig = {
  grantType: string;
  accessTokenEndpoint: string;
  queryParams?: { [index: string]: string };
  bodyParams?: { [index: string]: string };
  scopes?: string[];
  credentialsDir: string;
}
Toggle description
type AuthorizationCodeGrantConfig = {
  grantType: string;
  accessTokenEndpoint: string;
  queryParams?: { [index: string]: string };
  bodyParams?: { [index: string]: string };
  scopes?: string[];
  credentialsDir: string;
  code: string;
  redirectUri: string;
}
Toggle description
type PasswordCredentialsGrantConfig = {
  grantType: string;
  accessTokenEndpoint: string;
  queryParams?: { [index: string]: string };
  bodyParams?: { [index: string]: string };
  scopes?: string[];
  credentialsDir: string;
}
Toggle description
type RefreshGrantConfig = {
  grantType: string;
  accessTokenEndpoint: string;
  queryParams?: { [index: string]: string };
  bodyParams?: { [index: string]: string };
  scopes?: string[];
  credentialsDir: string;
  refreshToken: string;
}

Passing credentials explicitly

Toggle description

Instead of providing a credentials directory (credentialsDir) client and user credentials can be passed explicitly.

type ClientCredentialsGrantConfig = {
  grantType: string;
  accessTokenEndpoint: string;
  queryParams?: { [index: string]: string };
  bodyParams?: { [index: string]: string };
  scopes?: string[];
  clientId: string,
  clientSecret: string
}

Client credentials can be passed in via clientId and clientSecret, user credentials via applicationUsername and applicationPassword;

Token

Toggle description
type Token<CustomTokenPart = {}> = CustomTokenPart & {
  access_token: string;
  expires_in?: number;
  scope?: string[];
  token_type?: string;
  local_expiry?: number;
};

Token type it can be extend to satisfy special needs:

const mytoken: Token<{ id: number }> = {
  access_token: 'abcToken',
  id: 2424242828
}

Mock Tooling

This tooling provides an abstraction to easily mock OAuth 2.0 RFC 6749 related endpoints.

It helps to easily write integration tests without writing extensive boilerplate code and without disabling OAuth for tests.

This tooling is based on Nock, a HTTP mocking library. For more information about Nock see Nock documentation.

mockAccessTokenEndpoint

Toggle description

Creates a very basic mock of token endpoint as defined in RFC 6749.

The mocked endpoint will return a token with the scopes specified in the request.

  • ⚠️  The mock does not validate the request
  • ⚠️  The mock holds a state that contains the created tokens
  • ⚠️  cleanMock resets the state and removes all nocks

Usage

mockAccessTokenEndpoint({
  url: 'https://example.com/access_token',
  times: 1
});

Signature

mockAccessTokenEndpoint(options) => nock.Scope

Arguments

  • options:
    • url: string - URL of the Token validation endpoint
    • times?: number - Defines number of calls the endpoint is mocked, default is Number.MAX_SAFE_INTEGER

mockTokenInfoEndpoint

Toggle description

Creates a very basic mock of a token validation endpoint.

Returns 200 and a Token object if the given token is valid (via query parameter). The token is valid:

  • If it is specified via options parameter
  • If it was created by mockAccessTokenEndpoint

Return 400 if the given Token is invalid.

The optional tokens property in MockOptions can be used to restrict the list of valid access_tokens.

Usage

mockTokeninfoEndpoint(
  {
    url: 'https://example.com//token_validation',
    times: 1
  },
  tokens: [
    {
      access_token: 'someToken123',
      scope: ['uid', 'something.read', 'something.write']
    }
  ]
);

Signature

mockTokeninfoEndpoint(options, tokens) => nock.Scope

Arguments

  • options:
    • url: string - URL of the Token validation endpoint
    • times?: number - Defines number of calls the endpoint is mocked, default is Number.MAX_SAFE_INTEGER
  • tokens?: Token[] - List of valid tokens and their scopes.

cleanMock()

Toggle description
  • Remove all nock mocks (not only from this lib, really ALL)
  • Resets the token state object used by mockTokeninfoEndpoint and mockAccessTokenEndpoint and given tokens.

Hint: Helpful when having multiple tests in a test suite, you can call cleanMock() in the afterEach() callback for example.

Usage

cleanMock();

Mock error endpoints

Toggle description

To mock failing OAuth Endpoints use this mocks:

  • mockAccessTokenEndpointWithErrorResponse
  • mockTokeninfoEndpointWithErrorResponse

Usage

mockAccessTokenEndpointWithErrorResponse({
  url: 'https://example.com/access_token',
  times: 1
}, 401, { status: 'foo' });

Signature

mockAccessTokenEndpointWithErrorResponse(options, httpStatus, responseBody) => nock.Scope

Arguments

  • options:
    • url: string - URL of the Token validation endpoint
    • times?: number - Defines number of calls the endpoint is mocked, default is Number.MAX_SAFE_INTEGER
  • httpStatus: number - StatusCode of the response
  • responseBody?: object- Body of the response