Skip to content
This repository was archived by the owner on Sep 6, 2022. It is now read-only.

Latest commit

 

History

History
273 lines (196 loc) · 11.5 KB

oidc.md

File metadata and controls

273 lines (196 loc) · 11.5 KB

Using OpenId Connect and OAuth 2.0 for authentication and authorisation

Introduction

In the beginning, there were proprietary approaches to working with external identity providers for authentication and authorisation.

Then SAML (Security Assertion Markup Language) was designed as an open standard using SOAP and XML as its data format.

Then, there was OAuth and OAuth 2.0 using a RESTful approach to authorisation using JSON as its data format instead.

Today, the preferred way of dealing with “secure delegated access” is OpenID Connect (a.k.a OIDC).

OpenID Connect is an identity layer running on top of the OAuth 2.0 protocol, that specifies a RESTful HTTP API, using JSON as a data format.

It allows computing clients to verify the identity of an end-user based on the authentication performed by an authorisation server, as well as to obtain end-user profile information.

Tested Flows

OAuth 2.0 describes a number of grants (“methods”) for a client application to acquire an access token (which represents a user’s permission for the client to access their data) which can be used to authenticate a request to an API endpoint.

The specification describes five grants for acquiring an access token.

Onix has been tested with the two flows described below.

Authorisation Code Flow

The Authorisation Code Grant Type is probably the most common of the OAuth 2.0 grant types as it is used by both web and native applications to get an access token after a user authorises the application.

The flow has the following steps:

  • The application opens a browser to send the user to the OAuth server
  • The user sees the authorisation prompt and approves the application's request
  • The user is redirected back to the application with an authorisation code in the query string
  • The application exchanges the authorisation code for an access token

This flow is seamless to the user when using the Onix Web API for example opening the Swagger UI.

As the user hits the Swagger User Interface is redirected to the configured authorisation server. Upon entering their credentials and authenticating successfully, the user is redirected to the Swagger UI.

For more information on how thos flow works see here.

Resource Owner Password Flow

This flow is typically used by client applications which do not want to use a web browser, for example, when talking to the CMDB using Ansible modules or the Terraform provider.

The flow has the following steps:

  • The client application asks the user for their username and password.

  • The client then sends a POST request with following body parameters to the authorisation server:

    • grant_type with the value password
    • client_id with the the client’s ID
    • client_secret with the client’s secret
    • scope with a space-delimited list of requested scope permissions.
    • username with the user’s username
    • password with the user’s password

For example, as follows:

curl  --request POST \
      --url https://dev-447786.okta.com/oauth2/default/v1/token \ 
      --header 'accept: application/json' \
      --header 'Authorization: Basic MG9hbHloMHJ5U1J...' \
      --header 'cache-control: no-cache' \
      --header 'content-type: application/x-www-form-urlencoded' \
      --data 'grant_type=password&[email protected]&password=dhgcrce..&scope=openid%20onix'

NOTE: in order to authenticate the request, a basic access token is passed in the authorization header containing the resource server client_id and secret.

  • The authorisation server then responds with a JSON object containing the following properties:

    • token_type with the value Bearer
    • expires_in with an integer representing the TTL of the access token
    • access_token the access token itself
    • refresh_token a refresh token that can be used to acquire a new access token when the original expires
{
  "access_token":"eyJraWQiOiJuWlVs...dfg",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"onix openid",
  "id_token":"eyJra...ZOSUZrVGztg"
}

The access_token can be decoded using the jwt.io web site resulting in something like:

{
  "ver": 1,
  "jti": "AT.C6c9uWz4...WfQ...lJ5IAr1LJ...M",
  "iss": "https://dev-447786.okta.com/oauth2/default",
  "aud": "api://default",
  "iat": 15...7858,
  "exp": 15...1458,
  "cid": "0oalyh...gaj356",
  "uid": "00ujnm...zmY356",
  "scp": [
    "onix",
    "openid"
  ],
  "sub": "[email protected]",
  "roles": "ADMIN, WRITER"
}

NOTE: in the example, the token contains a roles claim with two roles ADMIN and WRITER. Onix maps these roles to the privilieges set in its database granting the user access accordingly.

  • Finally, the client can then send the bearer token with every request to the Web API, for example to retrieve item types calling the web api as follows:
curl  --request GET \
      --url http://localhost:8080/itemtype \
      --header 'accept: application/json' \
      --header 'authorization: Bearer eyJraWQiOiJuWlVs...dfg'

Configuring the Authorisation Server

In order to authenticate using an OpenId Connect provider, a client application and a authorisation server must be created in the Identity provider first.

As an example, in Okta do the following:

To create a new Application:

  • Go to Applications
  • Add Application
  • Select "Native" and click "Next".
  • Give it a name
  • Tick the "Resource Owner Password" box under Grant Type Allowed
  • Click done

Now the client_id and secret for the application can be retrieved from the Application General Tab.

The default resource server in Okta can be used.

  • Go to API and clieck on Authorisation Servers
  • Click on the "default" server link
  • Click on "Scopes" and add a new scope called onix
  • Click on "Claims" and add a new claim called roles
  • Select "include claim in access token"
  • Select "Value Type = Expression"
  • Type String.toUpperCase("admin, writer") as value
  • Under Include in section select "The following scopes"
  • Type onix as the scope

Configuring the Web API for OIDC

In addition, the following environment variables should be specified when launching the Onix WAPI application:

Variable Description
AUTH_MODE The authentication mode used by the Onix Web API, must be set it to "oidc".
OIDC_ISSUER The name of the token issuer.
OIDC_JWKURL The URI whwre the issuer has published the JavaScript Web Key certificates used to validate Java Web Tokens.
OIDC_REDIRECT_URI The resource server endpoint prfroming the login. Note: in the Onix Web API the endpoint is "/oidc-login".
OIDC_USER_AUTH_URI The URI of the authorisation server.
OIDC_ACCESS_TOKEN_URI The URI of the token resource.
OIDC_CLIENT_SECRET The client secret used to authenticate with the authorisation server.
OIDC_CLIENT_ID The unique identifier for the client application to be protected.

Note: client id and secret values are created when setting up a new client application in the OpenId provider.

Google OpenId API example

The following values apply to a google provider:

Variable Value
AUTH_MODE oidc
OIDC_ISSUER accounts.google.com
OIDC_JWKURL https://www.googleapis.com/oauth2/v2/certs
OIDC_REDIRECT_URI http://localhost:8080/oidc-login
OIDC_USER_AUTH_URI https://accounts.google.com/o/oauth2/auth
OIDC_ACCESS_TOKEN_URI https://www.googleapis.com/oauth2/v3/token
OIDC_CLIENT_SECRET 24 character string
OIDC_CLIENT_ID id_string + .apps.googleusercontent.com

Information on how to create an OpenId Connect application using Google OAuth 2.0 APIs can be found here.

OKTA OpenID API example

The following values apply to an Okta provider:

Variable Value
AUTH_MODE oidc
OIDC_ISSUER https://dev-447786.okta.com/oauth2/default
OIDC_JWKURL https://dev-447786.okta.com/oauth2/default/v1/keys
OIDC_REDIRECT_URI http://localhost:8080/oidc-login
OIDC_USER_AUTH_URI https://dev-447786.okta.com/oauth2/default/v1/authorize
OIDC_ACCESS_TOKEN_URI https://dev-447786.okta.com/oauth2/default/v1/token
OIDC_CLIENT_SECRET 40 character string
OIDC_CLIENT_ID 20 character string

Information on how to create an OpenId Connect application in Okta can be found here.

Authorising users

OpenID Connect employs OAuth 2.0 access tokens to allow client apps to retrieve user information from the UserInfo endpoint.

Additionally, access tokens can carry claims which are used by the protected resources to determine if the user has access to the resources.

Onix Scope

An OpenID provider for Onix Web API will have to add a new access token scope called onix.

The following picture shows how to add the new scope in Okta:

Creating an Onix scope

Roles Claim

In order to authorise logged on users, the OAuth 2.0 access token must contain a claim called roles under the onix scope, with a comma separated list of the role names granted to the user.

The following picture shows an example of adding a static roles claim in an Okta OAuth 2.0 access token:

Adding a role claim

Typically, the value of this claim should be dynamically determined based on the user group membership in the backing directory.

The access token should look like this:

{
  "ver": 1,
  "jti": "AT.0W3...DdQuL-qMGl...wuuK-u6Y...0I",
  "iss": "https://dev-447786.okta.com/oauth2/default",
  "aud": "api://default",
  "iat": 155....369,
  "exp": 155....969,
  "cid": "0oajnv4.....YRtO0356",
  "uid": "00ujnmh.....zmyXY356",
  "scp": [
    "openid",
    "onix"
  ],
  "sub": "[email protected]",
  "role": "ADMIN, WRITER"
}

The roles are then mapped within the Onix WAPI to data model logical partitions via Read, Write and/or Delete privileges according to the model described here.