OpenID Connect authorization

This UnityBase model implements authentication over OpenID Connect.

For users: Authorization Code Flow

Users from browsers can be authenticated using OpenID Authorization Code Flow

  • https://tools.ietf.org/html/rfc6749#section-4.1
  • https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow

For adminUI "OpenIDConnect" should be added to the "authenticationMethods".

Configuration:

"security": {
  "authenticationMethods": ["UB", "OpenIDConnect"]
}

Usage:

const openID = require('@unitybase/openid-connect')
let oIdEndPoint = openID.registerEndpoint('openIDConnect')
// Google
// see https://accounts.google.com/.well-known/openid-configuration to get Url`s
oIdEndPoint.registerProvider('Google', {
  authUrl: 'https://accounts.google.com/o/oauth2/auth',
  tokenUrl: 'https://accounts.google.com/o/oauth2/token',
  userInfoUrl: 'https://www.googleapis.com/oauth2/v1/userinfo',
  userInfoHTTPMethod: 'GET',
  scope: 'openid',
  nonce: '123',
  response_type: 'code',
  client_id: '350085411136-lpj0qvr87ce0r0ae0a3imcm25joj2t2o.apps.googleusercontent.com',
  client_secret: 'dF4qmUxhHoBAj-E1R8YZUCqA',
  getOnFinishAction: function (response) {
    return 'opener.$App.onFinishOpenIDAuth(' + JSON.stringify(response) + '); close();'
  },
  getUserID: function(userInfo) {
    let inst = UB.Repository('uba_user').attrs(['ID'])
       .where('[name]', '=', userInfo.id).select()
    return inst.eof ? null : inst.get('ID')
  }
})

// Hideez
oIdEndPoint.registerProvider('Hideez', {
  authUrl: 'https://xxx.hideez.com/connect/authorize',
  tokenUrl: 'https://xxx.hideez.com/connect/token',
  userInfoUrl: 'https://xxx.hideez.com/connect/userinfo',
  userInfoHTTPMethod: 'GET',
  scope: 'openid email', //email - omportant 
  //scope: 'openid email phone roles profile',
  nonce: 'replaceToSomeRandom',
  response_type: 'code',
  client_id: '.....',
  client_secret: '....',
  getUserID: function(userInfo) {
    let inst = UB.Repository('uba_user').attrs(['ID'])
      .where('[name]', '=', (userInfo.email || '-').toLocaleLowerCase()).select().
      return inst.eof ? null : inst.get('ID')
  }
})

// Azure
// see https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration to get Url`s
oIdEndPoint.registerProvider('Azure', {
  authUrl: 'https://login.microsoftonline.com/{tenant-guid}/oauth2/v2.0/authorize',
  tokenUrl: 'https://login.microsoftonline.com/{tenant-guid}/oauth2/v2.0/token',
  userInfoUrl: 'https://graph.microsoft.com/oidc/userinfo',
  userInfoHTTPMethod: 'POST',
  scope: 'openid',
  nonce: '123',
  response_type: 'code',
  response_mode: 'query',
  client_id: 'YOUR_CLIENT_ID',
  client_secret: 'YOUR_CLIENT_SECRET',
  getUserID: function(userInfo) {
    // console.debug("userinfo=", JSON.stringify(userInfo))
    let inst = UB.Repository('uba_user').attrs(['ID'])
      .where('[name]', '=', userInfo.name).select()
    console.debug("user", inst.eof ? null : inst.get('ID'))
    return inst.eof ? null : inst.get('ID')
  }
})

For Machine2Machine

To communicate between UnityBase server and other services, what uses OpenID first - register a provider, for example for keyCloak:

const openID = require('@unitybase/openid-connect') 
const keyCloakBaseUrl = 'http://localhost:8080/realms/myrealm/protocol/openid-connect'
openID.registerProvider('keyCloak', {
  authUrl: keyCloakBaseUrl + '/auth',
  tokenUrl: keyCloakBaseUrl + '/token',
  userInfoUrl: keyCloakBaseUrl + '/userinfo',
  logoutUrl: keyCloakBaseUrl + '/logout',

  client_id: 'oidKeycloak',

  client_secret: 'CLkwDlhcHGIwFh52HbDJF0mPk1jSdHYa',

  userInfoHTTPMethod: 'POST',
  scope: 'openid',
  nonce: '',
  response_type: 'code',
  response_mode: 'query'
})

and then use one of available methods to get an Aunthorisation header value for your API calls

Client Credentials Flow

  • https://tools.ietf.org/html/rfc6749#section-4.4
  • https://auth0.com/docs/get-started/authentication-and-authorization-flow/client-credentials-flow

Clients Credentials grant type must be enabled on OpenID provider side ("Service accounts roles" for KeyCloak provider)

const authVal = openID.provider('keyCloak').getCCToken()
UB.get('https://some.server/with/auth', {
  headers: {
    Authorization: authVal
  }
})

Resource Owner Password Flow

username and password MUST be stored in secure way on server. NEVER pass this values from client

const authVal = openID.provider('keyCloak').getROPToken(username, password)
UB.get('https://some.server/with/auth', {
  headers: {
    Authorization: authVal
  }
})

Methods

# openIDConnectEp (reqTHTTPRequest, respTHTTPResponse) inner

OpenID Authorization Code Flow (user interaction) endpoint implementation

  • https://tools.ietf.org/html/rfc6749#section-4.1
  • https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow

If called as /endpoint - return a list of registered openIDConnect providers, If called as /endpoint/provider:

  • without parameters or with mode === 'auth' - redirect to provider authUrl
  • with parameters code and state - call doProviderAuthHandshake method
  • with parameters logout - redirect to log out url

Arguments:

# provider (providerNamestring) → OpenIdProvider | undefined inner

Return provider by name

Arguments:

# registerOpenIDEndpoint (endpointNamestring) → openIDEndpoint inner

Register openID connect endpoint. In case endpoint already registered - return existed

Return:

endpoint

Arguments:

# registerOpenIDProvider (namestring, providerConfigProviderConfig) inner

Register OpenID provider

Arguments:

Types

# openIDEndpoint inner

OpenID endpoint. Able to register providers

Properties