OIDC OpenID Connect - Lightwave

 

OAuth 2.0 / OpenID Connect in Lightwave


  1. Introduction

Lightwave OIDC serves two main purposes.

1. Security Token Service (STS) in the sense that it issues security tokens in the form of JWT tokens (similar to WS-TRUST which issues SAML 2.0 Assertions)

2. single signon (SSO) / single logout (SLO) service for web-based applications (similar to SAML 2.0 Authentication Request and Single Logout protocols aka websso)

 

Implementation (Open source)

  https://github.com/vmware/lightwave/tree/dev/vmidentity/openidconnect

 

  1. Roles

User (Resource Owner)

User agent (browser)

Client (aka Relying Party / RP) e.g: lightwaveui

Authz Server (aka OpenID Provider / OP)

Resource Server (server that wants to implement authz for its API's) e.g: admin server

  1. JWT tokens

OAuth 2.0 defines an access_token and a refresh_token but does not specify their format. We are using JWT as the format.

OpenID Connect additionaly defines an id_token and specifies its format as a JWT.

Lightwave OIDC issues id_tokens, access_tokens, and refresh_tokens. All will be signed using the tenant's private key.

  1. id_token

Represents the authentication of an end user at the Authz Server. The Client will validate this token and read its claims to sign in the end user.

example id_token JSON body:


{

  "sub": "administrator@lw-testdom.com",

  "iss": "https://photon-63mciz57.lw-testdom.com/openidconnect/LW-TESTDOM.COM",

  "groups": [

    "LW-TESTDOM.COM\\Users",

    "LW-TESTDOM.COM\\Administrators",

    "LW-TESTDOM.COM\\CAAdmins",

    "LW-TESTDOM.COM\\Everyone"

  ],

  "token_class": "id_token",

  "token_type": "Bearer",

  "given_name": "Administrator",

  "aud": "administrator@lw-testdom.com",

  "scope": "at_groups rs_admin_server openid offline_access id_groups",

  "exp": 1501698283,

  "iat": 1501694683,

  "family_name": "LW-TESTDOM.COM",

  "jti": "rbdevuzSlBfwgSvQJdgtviOeVUmidOHcDZw79iy6ouQ",

  "tenant": "LW-TESTDOM.COM"

}

  1. access_token

The Client will use this in its protected resource requests against the Resource Server.

The Resource Server, on receiving the access_token from the Client, will validate the token and make an API authz decision based on the user's group membership in the groups claim.

example access_token JSON body:

{

  "sub": "administrator@lw-testdom.com",

  "aud": [

    "administrator@lw-testdom.com",

    "rs_admin_server"

  ],

  "scope": "at_groups rs_admin_server openid offline_access id_groups",

  "iss": "https://photon-63mciz57.lw-testdom.com/openidconnect/LW-TESTDOM.COM",

  "groups": [

    "LW-TESTDOM.COM\\Users",

    "LW-TESTDOM.COM\\Administrators",

    "LW-TESTDOM.COM\\CAAdmins",

    "LW-TESTDOM.COM\\Everyone"

  ],

  "token_class": "access_token",

  "token_type": "Bearer",

  "exp": 1501698283,

  "iat": 1501694683,

  "jti": "0lXE9y17uMevd0gBaIhRZ1ZqoYb7P64EuY7Bh7qyPXY",

  "tenant": "LW-TESTDOM.COM",

  "admin_server_role": "Administrator"

}

  1. refresh_token

The Client uses this token to get a new access_token (and id_token) when the original access_token expires (as reported by a Resource Server).

example refresh_token JSON body:


{

  "sub": "administrator@lw-testdom.com",

  "aud": "administrator@lw-testdom.com",

  "scope": "at_groups rs_admin_server openid offline_access id_groups",

  "iss": "https://photon-63mciz57.lw-testdom.com/openidconnect/LW-TESTDOM.COM",

  "token_class": "refresh_token",

  "token_type": "Bearer",

  "exp": 1501723483,

  "iat": 1501694683,

  "jti": "tKtIlifxWfgt8YpDZ0mwq8uF2xYxANlDnucHIOn7IAI",

  "tenant": "LW-TESTDOM.COM"

}


  1. Supported Flows

    1. Authz Code Flow

sequence diagram:

sample request/response:


Authn Request

    GET /authorize?

        response_type=code&

        client_id=_client_id_xyz_&

        redirect_uri=https://client.example.com/cb&

        state=_state_xyz_&

        nonce=_nonce_xyz_&

        scope=openid offline_access HTTP/1.1

    Host: server.example.com


Authn Response

    HTTP/1.1 302 Found

    Location: https://client.example.com/cb?

        code=_authz_code_xyz_&

        state=_state_xyz_


Token Request

    POST /token HTTP/1.1

    Host: server.example.com

    Content-Type: application/x-www-form-urlencoded


    grant_type=authorization_code&

    code=_authz_code_xyz_&

    redirect_uri=https://client.example.com/cb&

    client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&

    client_assertion=<private_key_jwt>


Token Response

    HTTP/1.1 200 OK

    Content-Type: application/json;charset=UTF-8

    Cache-Control: no-store

    Pragma: no-cache


    {

        "access_token":"<access_token>",

        "token_type":"Bearer",

        "expires_in":3600,

        "id_token":"<id_token>",

        "refresh_token":"_refresh_token_xyz_"

    }

  1. Implicit Flow

  1. sequence diagram:

  2. sample request/response:

    1. Authn Request

    2.     GET /authorize?

    3.         response_type=id_token&

    4.         client_id=_client_id_xyz_&

    5.         redirect_uri=https://client.example.com/cb&

    6.         state=_state_xyz_&

    7.         nonce=_nonce_xyz_&

    8.         scope=openid HTTP/1.1

    9.     Host: server.example.com


    10. Authn Response

    11.     HTTP/1.1 302 Found

    12.     Location: http://example.com/cb#

    13.         state=_state_xyz_&

    14.         id_token=<id_token>

    15. Password Flow

  3. sequence diagram:

  4. sample request/response:


    1. Token Request

    2.     POST /token HTTP/1.1

    3.     Host: server.example.com

    4.     Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

    5.     Content-Type: application/x-www-form-urlencoded


    6.     grant_type=password&

    7.     username=_username_xyz_&

    8.     password=_password_xyz_&

    9.     scope=openid offline_access


    10. Token Response

    11.     HTTP/1.1 200 OK

    12.     Content-Type: application/json;charset=UTF-8

    13.     Cache-Control: no-store

    14.     Pragma: no-cache


    15.     {

    16.         "access_token":"<access_token>",

    17.         "token_type":"Bearer",

    18.         "expires_in":3600,

    19.         "id_token":"<id_token>",

    20.         "refresh_token":"_refresh_token_xyz_"

    21.     }

    22. Refresh Token Flow

  5. sequence diagram:

  6. sample request/response:


    1. Token Request

    2.     POST /token HTTP/1.1

    3.     Host: server.example.com

    4.     Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

    5.     Content-Type: application/x-www-form-urlencoded


    6.     grant_type=refresh_token&

    7.     refresh_token=_refresh_token_xyz_&

    8.     client_id=_client_id_xyz_


    9. Token Response

    10.     HTTP/1.1 200 OK

    11.     Content-Type: application/json;charset=UTF-8

    12.     Cache-Control: no-store

    13.     Pragma: no-cache


    14.     {

    15.         "access_token":"<access_token>",

    16.         "token_type":"Bearer",

    17.         "expires_in":3600,

    18.         "id_token":"<id_token>"

    19.     }


  1. Metadata endpoint


Spec link: http://openid.net/specs/openid-connect-discovery-1_0.html


OIDC Metadata endpoint publishes a JSON format document that includes server metadata such as server endpoints and server supported capability configurations. Client can use these data to help construct requests used in OIDC workflows.


Metadata endpoint format:


https://[OIDC Server URL]/{tenant}/.well-known/openid-configuration


Request to the metadata endpoint should use HTTP verb: GET. A sample request is listed below:


GET [OIDC Server URL]/{tenant}/.well-known/openid-configuration HTTP/1.1

Host: example.com


A successful response will return HTTP status code 200 (OK), and a JSON object using the application/json content type. The details for claim definitions can be found in the spec above. A sample response is listed below:


HTTP/1.1 200 OK

Content-Type: application/json

{  

   "response_types_supported":[  

      "code",

      "id_token",

      "token id_token"

   ],

   "jwks_uri":"https:\/\/photon-63mciz57.lw-testdom.com\/openidconnect\/jwks\/LW-TESTDOM.COM",

   "end_session_endpoint":"https:\/\/photon-63mciz57.lw-testdom.com\/openidconnect\/logout\/LW-TESTDOM.COM",

   "subject_types_supported":[  

      "public"

   ],

   "id_token_signing_alg_values_supported":[  

      "RS256"

   ],

   "issuer":"https:\/\/photon-63mciz57.lw-testdom.com\/openidconnect\/LW-TESTDOM.COM",

   "authorization_endpoint":"https:\/\/photon-63mciz57.lw-testdom.com\/openidconnect\/oidc\/authorize\/LW-TESTDOM.COM",

   "token_endpoint":"https:\/\/photon-63mciz57.lw-testdom.com\/openidconnect\/token\/LW-TESTDOM.COM"

}


An error response will return HTTP status code 404 (Not Found) or 500 (Internal Server Error).


  1. JWKS endpoint

OIDC server also provides a JWK set endpoint to publish OIDC server public RSA keys in the format of JSON Web Key (JWK) set. The spec for JWK can be found in the link: https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41.


Relying parties and resource servers need to retrieve this public key so they can verify token signature signed by the OIDC server.


JWK set endpoint format:


https://[OIDC Server URL]/jwks/{tenant}


Request to the JWK set endpoint should use HTTP verb: GET. A sample request is listed below:


GET [OIDC Server URL]/jwks HTTP/1.1

Host: example.com


A successful response will return HTTP status code 200 (OK), and a JSON object using the application/json content type. A sample response is listed below:


HTTP/1.1 200 OK

Content-Type: application/json

{

 "keys":

 [

  {

   "e": "AQAB",

   "n": "......",

   ...... 

  }

 ]

}


An error response will return HTTP status code 404 (Not Found) or 500 (Internal Server Error).

  1. Holder Of Key tokens

Solution Users or Clients that have a backing Solution User can request HOK tokens. The token request will include a proof of possession of the private key in the form of a solution_user_assertion or client_assertion which are JWT's signed using the private key.

OIDC server will use the Solution User's registered certificate to validate the signature of the JWT.

The issued access_token will contain an "hotk" claim that is a JWK Set containing the Solution User's public key.

The Client will then send signed requests to Resource Servers that support HoK access_tokens.

The Resource Server will validate the signature of the request using the public key in the access_token's "hotk" claim.


  1. Act As tokens

ActAs tokens are HOK tokens whose subject is a Person User. A token request that includes PersonUser and SolutionUser credentials at the same time will result in ActAs tokens.

There are many forms  of Person User credentials, for example username/password or authorization code.

Solution User credentials will be in the form of solution_user_assertion or client_assertion which are both JWT's signed using the Solution User's private key.

The most common form of acquiring ActAs tokens is the Authorization Code Flow. The authorization code represents Person User credentials (the user who logged) and the token request is required to have a client_assertion which represents a registered OIDC client with a backing Solution User. 

  1. Login

When a user is redirected to the Authz Server, if the Authz Server does not receive a session cookie, it will serve a login form.

The user can then login using username/password (or RSA SecurID, Kerberos, Smart Card, if enabled).

Single Sign On

SSO is achieved by the use of cookies.

When a user logs in to the Authz Server (after being redirected to it by a Client), a session cookie is returned " oidc_session_id-<tenant>=<session_id>".

When the user attempts to login to another Client, the other Client will redirect the user to the same Authz Server, the browser will then include the Authz Server session cookie which will allow the Authz Server to login the same user without prompting for credentials.

  1. Logout

  2. Implemented according to OpenID Connect Session Management 1.0 - draft 23 Section 5. "RP-Initiated Logout"

  3. sequence diagram:

  4. sample request/response:


    1. Logout Request

    2.     GET /logout?

    3.         id_token_hint=<id_token>&

    4.         post_logout_redirect_uri=https://client.example.com/cb&

    5.         state=_state_xyz_

    6.     Host: server.example.com

    7.     Cookie:oidc_session_id-vsphere.local=<session_id>


    8. Logout Response

    9.     HTTP/1.1 302 Found

    10.     Location: https://client.example.com/cb?

    11.         state=_state_xyz_

    12.     Set-Cookie:oidc_session_id-vsphere.local=""

Single Log Out

  1. Implemented according to OpenID Connect HTTP-Based Logout 1.0 - draft 00

  2. When Authz Server receives a logout request, it returns an html page with iframe tags whose source is the logout_uri of all the Clients the user has logged into.

  3. The browser will make HTTP GET requests and include session cookies which will allow the Client to logout the user.


Implementation

Server State

The following classes keep track of server state using LinkedHashMaps (wrapped by oidc.server.SlidingWindowMap):


class

purpose

map key

map value

SessionManager

HTTP session cookie maps to a user logged in to a number of Clients

SessionID


PesonUser, LoginMethod, Set<ClientID>, Set<ClientInfo>

AuthorizationCodeManager

state for issued authz codes

AuthorizationCode

PersonUser, SessionID, original AuthenticaitonRequest


Request Processing

Every endpoint has a Controller which is the first point of processing:


MetadataController

JWKSController

AuthenticationController

TokenController

LogoutController


Metdata and jwks requests are simple GET requests and will be handled directly by their corresponding Controller whereas the rest will be handled by RequestProcessor's:


AuthenticationRequestProcessor

TokenRequestProcessor

LogoutRequestProcessor


These will encapsulate requests and responses:


AuthenticationRequest

AuthenticationResponse

AuthenticationSuccessResponse

AuthenticationErrorResponse


TokenRequest

TokenResponse

TokenSuccessResponse

TokenErrorResponse


LogoutRequest

LogoutResponse

LogoutSuccessResponse

LogoutErrorResponse


Code Organization

Code is organized into 5 components that build into separate jars


component

role

dependencies

server

request processing, state management

common, protocol

client

client library

common, protocol

protocol

request and response encapsulation, token definition

common

common

public types and enums


sample

sample Client/Relying Party, builds into a war file

common, client


Data Sheet

Endpoints

endpoint

supported?

usage

authorization_endpoint

interactive user login

token_endpoint

obtain id_token,access_token,refresh_token

userinfo_endpoint


obtain user profile information

jwks_uri

obtain public key corresponding to the private signing key (per-tenant)

discovery

OIDC Discovery

registration_endpoint


OIDC Dynamic Client Registration

end_session_endoint

logout

check_session_iframe


a way of implementing single logout


Response types

response_type

supported?

usage

code

authz code flow

id_token

implicit flow

id_token token

implicit flow

token


implicit flow (OAuth 2.0)

code id_token


hybrid flow

code id_token token


hybrid flow

code token 


hybrid flow


Grant types

grant_type

supported?

usage

authorization_code

authz code flow

implicit

implicit flow

password

password flow

refresh_token

refresh token flow

certificate

extension grant type

gss ticket

extension grant type

securid

extension grant type

perosn user certificate

extension grant type

client_credentials

client credentials flow

JWT


JWT Profile for Authz Grants

SAML 2.0


SAML 2.0 Profile for Authz Grants


Subject identifier types

subject_type

supported?

public

pairwise



Client authentication methods

token_endpoint_auth_method

supported?

client_secret_basic


client_secret_post


client_secret_jwt


private_key_jwt


JWT signature algorithms

signature algorithm

supported?

RS256

RS384


RS512


PS256


PS384


PS512



Error Codes


OAuth2 .0 error code

returned?



invalid_request

unauthorized_client

invalid_client

invalid_scope

unsupported_response_type

unsupported_grant_type

invalid_grant

access_denied

server_error

temporarily_unavailable




OpenID Connect error code




interaction_required


login_required


account_selection_required


consent_required


invalid_request_uri


invalid_request_object


request_not_supported


request_uri_not_supported


registration_not_supported






Comments

Popular posts from this blog

sde-interview-ramp-up

Installing YUM on OpenSUSE

Youtkit - Java Profiler