Upstream Service - Authentication

Overview

Upstream service allows user to obtain an accessToken that is used for intra-service
communication over HTTP and WebSocket connections in Platform infrastructure.

Obtaining a Token

Client needs to obtain access token before calling services. Token is generated as
a part of successful login.

Before doing the login, client must select identity provider as an authority that
handles the login / verifies user credentials. Potentially, there are many identity
providers, so client must fetch list of available providers, “select” one of them,
do the login against a chosen provider in the provider-specific way.

This overall process is driven by hypermedia. Authentication is described by media
type based on Hypertext Application Language.

See sections below on how to get to the entry point, how to retrieve identity
providers and do specific login procedures.

Entry Point Resource

This resource is available under $UPSTREAM_URL/auth URL. This is the only
authentication-related URL that client must be aware of and is free to bind to.

Authentication entry point resource is a gateway to authentication functionality
holding links to related resources:

application/hal+json"
{
  "_links": {
    "curies": [
      {
        "name": "auth",
        "href": "http://~/auth/def/rels/{rel}",
        "templated": true
      }
    ],
    "self": {
      "href": "http://~/auth"
    },
    "auth:identity-providers": [
      {
        "href": "http://~/auth/identity-providers"
      }
    ],
    "auth:logout": [
      {
        "name": "reactive",
        "href": "http://~/auth/logout/reactive"
      },
      {
         "name": "reactive-tpl",
         "href": "http://~/auth/logout/reactive{?return}",
         "templated": true
      }
    ],
    "auth:oauth2-token": [
       {
        "name": "refresh"
        "href": "http://~/auth/oauth2/token"
       }
    ]
    "auth:sso-login": [
      {
        "name": "start-auto"
        "href": "http://~/auth/sso/login/start/auto"
      },
      {
        "name": "start-auto-tpl"
        "href": "http://~/auth/sso/login/start/auto{?IdpAdapterId}",
        "templated": true
      },
      {
        "name": "SAML2"
        "href": "http://~/auth/sso/login/saml2"
      }
    ],
    "auth:token": [
      {
        "name": "current"
        "href": "http://~/auth/tokens/current"
      }
    ]
  }
}

Supported media type of the representation is
application/hal+json”.

Note presence of the link to identity providers resource via auth:identity-providers relation.
If this link is available then client may fetch list of identity providers which
can be used to do the login.

NOTE: Client MUST NOT have any prior knowledge about URLs in the links mentioned
above as well as rely on its presence all the time. All the client MUST understand
is that presence of a link with relation auth:identity-providers enables discovery
of available identity providers which may be then found on the supplied URL. Link
is optional and its absence means that identity providers resource is not available
for some reason. Same rule applies for any other link described in this document.

Identity Providers

This resource is represented by aforementioned auth:identity-providers link
relation carrying information about available identity providers:

application/hal+json"
{
  "_links": {
    "curies": [
      {
        "name": "auth",
        "href": "http://~/auth/def/rels/{rel}",
        "templated": true
      },
      {
        "name": "auth-oauth",
        "href": "http://~/auth/identity-providers/kind/oauth/def/rels/{rel}",
        "templated": true
      },
      {
        "name": "auth-ping-federate",
        "href": "http://~/auth/identity-providers/kind/ping-federate/def/rels/{rel}",
        "templated": true
      }
    ],
    "self": {
      "href": "http://~/auth/identity-providers"
    }
  },
  "_embedded": {
    "auth:identity-provider": [
    {
        "kind": "oauth",
        "_links": {
            "auth-oauth:ropc-myavid": [{
                "href": "https://~/auth/sso/login/oauth2/ropc/myavid"
            }]
        }
    },
    {
        "kind": "oauth",
        "_links": {
            "auth-oauth:ropc-ad": [{
                "href": "https://~/auth/sso/login/oauth2/ad"
            }]
        }
    },
    {
        "kind": "oauth",
        "_links": {
            "auth-oauth:ropc-default": [{
                "href": "https://~/auth/sso/login/oauth2/ad"
            }]
        }
    },
    {
        "kind": "ping-federate",
        "_links": {
            "auth-ping-federate:idp-sso": [{
                "href": "https://~/idp/startSSO.ping{?PartnerSpId,TargetResource,InErrorResource,Binding,ACSIdx,IdpAdapterId,RequestedFormat}",
                "templated": true
            }]
        }
    }
  ] 
  }
}

Supported media type of the representation is
application/hal+json”.

Identity providers are described by embedded resources under auth:identity-provider
relation. Each subresource has a kind that is used to distinguish between different
identity provider kinds.

Identity providers are an actual authorities that handle the login. Authentication can
be done according to specific flow of the system or via OAuth2 authentication standard.
List of identity providers may vary depends on particular set up configuration.

Obtaining a Token. Resource Owner Password Credentials Grant

You can obtain a token by sending a HTTP POST request to the oauth2 kind identity provider endpoints with
content-type of application/x-www-form-urlencoded and payload described in RFC.
So, according to RFC, the payload MUST contain fields password=my_password, username=my_username and
grant_type="password". Also, the request MUST have Authorization header. The Authorization header MUST
contain Base 64 encoded payload < client_id:client_secret > (client_id and client_secret allows to identify
particular application and should be requested ahead)

*NOTE: There could be multiple identity providers with oauth-ropc kind for particular setup. Recommended to use link under
“auth-oauth:ropc-default” relation

NOTE: By default in the successful response to login request, a user will get access_token and the refresh_token.
The last one allows a client to use a login API more conveniently because bypassing refresh_token from first successful
login will allow getting a new access_token without bypassing user credentials. Getting an access token in the response
to login request is configurable. This option is enabled by default and can be disabled by passing no_refresh_token=true
parameter in the login form.

Example

Request → ← Response
POST /auth/sso/login/oauth2/ad HTTP/1.1
Content-Type: application/x-www-form-urlencoded
... 
username=< username > 
password=< password > 
grant_type=password
...
Authorization: Basic < client_id:client_secret >
HTTP/1.1 200 OK
Content-Type: application/json
...
{
    "access_token": "YWU0NTIyYjgtNzFiNC00MzczLTkxMGItN2I0Yjg2NjZmODg2",
    "expires_in": 86400,
    "token_type": "bearer",
    "refresh_token": "05a90614-3bf2-43b9-ae9b-d72442d81e92",
    "scope": "avid.collaboration/* avid.iam/findPrincipal"
}

Obtaining a Token via refresh token. Resource Owner Password Credentials Grant

You can obtain a token by sending a HTTP POST request to the oauth2 identity provider endpoints with
content-type of application/x-www-form-urlencoded and payload described in RFC.
So, according to RFC, the payload MUST contain fields grant_type="refresh_token" and
refresh_token=my_refresh_token. Also, the request MUST have Authorization header. The Authorization header MUST
contain Base 64 encoded payload < client_id:client_secret > (client_id and client_secret allows to identify
particular application and should be requested ahead).

The refresh token that is required in request payload can be obtained from the successful response to the login request.

Example

Request → ← Response
POST /auth/sso/login/oauth2/ad HTTP/1.1
Content-Type: application/x-www-form-urlencoded
... 
grant_type=refresh_token
refresh_token=< my_refresh_token > 
...
Authorization: Basic < client_id:client_secret >
HTTP/1.1 200 OK
Content-Type: application/json
...
{
    "access_token": "MjM2MTJkODMtNTNmNS00YWQzLTk5NTAtNDdjMjM0YjlhM2Rj",
    "expires_in": 86400,
    "token_type": "bearer",
    "scope": "*/*"
}

Obtaining an OpenID Connect token

OpenID Connect is an identity layer on top of the OAuth 2.0 protocol.
It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.

The endpoint is a part of OAuth 2 flow.

OpenID Connect works on top of resource owner password credentials and client credentials grant flows.
OpenID Connect introduces additional parameters to the standard Oauth2 requests:

  • new request parameter ‘scope’=’openid’
  • new response field ‘id_token’

Example of OpenID Connect on top of Resource Owner Password Credentials flow

Request → ← Response
 POST /auth/sso/login/oauth2/ad HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 ... 
 username=< username > 
 password=< password > 
 grant_type=password
 scope=openid
 ...
 Authorization: Basic < client_id:client_secret >
 
 HTTP/1.1 200 OK
 Content-Type: application/json
 ...
 {
     "access_token": "YWU0NTIyYjgtNzFiNC00MzczLTkxMGItN2I0Yjg2NjZmODg2",
     "expires_in": 86400,
     "token_type": "bearer",
     "refresh_token": "05a90614-3bf2-43b9-ae9b-d72442d81e92",
     "scope": "avid.collaboration/* avid.iam/findPrincipal",
     "id_token": "eyJhbGciO..."
 }
 

Example of using refresh token to obtain OpenID Connect token

Request → ← Response
  POST auth/oauth2/token HTTP/1.1
  Content-Type: application/x-www-form-urlencoded
  ... 
  grant_type=refresh_token
  scope=openid
  refresh_token=< my_refresh_token >
  ...
  Authorization: Basic < client_id:client_secret >
  
  HTTP/1.1 200 OK
  Content-Type: application/json
  ...
  {
    "access_token": "MTY3YmE3OWEtZDBmNS00MmVmLTllMGItODM2ZmU0ODQxZGQy",
    "expires_in": 86400,
    "token_type": "bearer",
    "scope": "avid.example/*",
    "id_token": "eyJhbGciO..."
  }
  

Renew OpenId token in the site of origin

If client, possessing an OpenId token in the site where token was originally requested (site of token origin), wants to make calls
with OpenId token after its expiration time without asking user for credentials again, it can renew its OpenId token periodically.

IMPORTANT: Client can not currently renew its OpenId token in the site other than site of token origin!

Client can find OpenId token renewal endpoint reference in /auth entry point HAL structure by looking for auth:oidc-token node.

In order to renew its OpenId token client sends request to respective endpoint providing its OpenId token in the body of the request.
Client must send its scope (id and secret) as a base64 basic authorization header.

IMPORTANT: OpenId Connect token is immutable, therefore as a result of a token renew flow described here,
client will receive response with a new token and expiration time. Client must “forget” its old token and use new one after exchange.

Example of OpenID Connect token renewal

Request → ← Response
  POST auth/id-token HTTP/1.1
  Content-Type: application/json
  ... 
  {
   "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
  ...
  Authorization: Basic < client_id:client_secret >
  
  HTTP/1.1 200 OK
  Content-Type: application/json
  ...
  {
   "id_token": "I1NiIsInIUzI1NiIsInsasaR5cCI6IkFGV56...",
   "expires_in": 172799
  }
  

Using PingFederate as Identity Provider

Client interested in the login via PingFederate SHOULD find auth:identity-provider
embedded resource of kind ping-federate. Hypertext reference identified within the
auth-ping-federate:idp-sso link points to the PingFederate’s resource for
idP-initiated SSO, i.e. /idp/startSSO.ping
resource. This reference is templated describing allowed query parameters.

In a typical case, client just redirects the user to that URL giving PartnerSpId,
ACSIdx, IdpAdapterId and TargetResource. All of these parameters are usually
pre-determined by the configuration but can also be changed by the client on demand:

  • PartnerSpId identifies the federation ID of the Service Provider to whom the
    SAML response containing an assertion should be issued.
  • ACSIdx specifies the index number of partner’s Assertion Consumer Service URL.
  • IdpAdapterId allows the client to call out specific adapter to use for
    authentication (in a configuration with multiple IdP adapters).
  • TargetResource is an indicator for a desired resource at the SP during
    IDP-initiated SSO.

For example, issuing the following URL:

https://ssotst.avid.com/idp/startSSO.ping?PartnerSpId=everywhere.avid.com&TargetResource=http://myapp/logged-in

will initiate the login procedure against PingFederate using everywhere.avid.com as
federation ID of the Service Provider. Following things will happen:

  • Showing login screen to the user (if the user is not logged in yet). If login is
    successful then:
    • POSTing credentials to the Upstream. If handling is successful then:
      • avidAccessToken session cookie is created and set on the Upstream’s
        domain - further requests to the Upstream will be authenticated
        unless token times out.
      • Client gets redirected to the target resource (http://myapp/logged-in).

This PingFederate server that can be used for development purposes:

https://ssotst.avid.com/idp/startSSO.ping?PartnerSpId=everywhere.avid.com&ACSIdx=1

By default, it will redirect to the Upstream residing on http://localhost:3001. Respective Upstream configuration:

/etc/sysconfig/avid-upstream
UPSTREAM_AUTH_PING_FEDERATE_HOST=ssotst.avid.com
UPSTREAM_AUTH_PING_FEDERATE_PARTNER_SP_ID=everywhere.avid.com
UPSTREAM_AUTH_PING_FEDERATE_ACS_IDX=1

Configuring Certificate for SAML Response Signature Validation

Upstream receives SAML response from PingFederate on successful user login. This
response carries verification from identity provider that the user is allowed to
access particular resource alongside with some user information. Upstream needs to
validate signature of this response against a certificate to prove that it is
coming from the trusted party.

UPSTREAM_AUTH_PING_FEDERATE_CERTIFICATE variable in /etc/sysconfig/avid-upstream
configuration file is used to setup location of the PingFederate certificate.
Currently, this property is commented and is pointing to PingFederate’s test
certificate, which is shipped with Upstream RPM for development purposes:

/etc/sysconfig/avid-upstream
#UPSTREAM_AUTH_PING_FEDERATE_CERTIFICATE=/opt/avid/etc/avid-upstream/avid-pf-tst.crt

Uncommenting it will enable signature verification against the test certificate which
is valid for PingFederate environment setup on ssotst.avid.com.

Passing and Verifying Access Token

Upstream supports token-based authentication check using one of the following methods (declared in the order of the Upstream’s selection preference):

  • Authorization HTTP response header with Bearer authentication scheme (recommended way).
  • _avidAccessToken query parameter.
  • avidAccessToken cookie.

Given that xyz is a valid token all requests below are treat as coming from authenticated party:

Authorization Header (recommended)
GET /apis/service/resource HTTP/1.1
<...>
Authorization: Bearer xyz
<...>
Query Parameter
GET /apis/service/resource?_avidAccessToken=xyz HTTP/1.1
<...>
Cookie
GET /apis/service/resource HTTP/1.1
<...>
Cookie: avidAccessToken=xyz
<...>

Passing and Verifying OpenID Connect Token

Upstream token-based authentication for OpenID is the same as for the Access Token with one exception, that is ID token can not be passed via cookie.

Client might pass along both accessToken (in a cookie) and OpenID token (as a header or query parameter) at the same time,
in such case OpenID token will be preferred and authentication will be done based on ID token.

It is recommended to pass OpenID token via authorization header.

Authorization Header
GET /apis/service/resource HTTP/1.1
<...>
Authorization: Bearer xyz
<...>

Obtaining Current Token

It is possible to retrieve current access token, OpenID token (if it was requested as part of authentication procedure) and IAM token associated with the
current token via auth:token named current which may be found in the entry point
resource:

{
    "_links": {
        "curies": {
            "name": "auth",
            "href": "http://~/auth/def/rels/{rel}",
            "templated": true
        }
    },
    <...>,
    "auth:token": [
        {
            "name": "current"
            "href": "http://~/auth/tokens/current"
        }
    ],
    <...>
}

Sample response:

{
  "_links": {
    "curies": [
      {
        "name": "auth",
        "href": "http://~/auth/def/rels/{rel}",
        "templated": true
      },
      {
        "name": "auth-token",
        "href": "http://~/auth/tokens/rels/{rel}",
        "template": true
      },
      "self": {
        "href": "http://~/auth/tokens/current"
      },
      "auth-token:extend": [
        {
            "href": "http://~/auth/tokens/current/extension"
        },
      "auth-token:removal": [
        {
            "href": "http://~/auth/tokens/current"
        }
    ]
  },
  "accessToken": "OWQzZWQyOGEtYWQ1YS00ZjU2LWJkMWUtMGM2NTEwMmUzMjkx",
  "iamToken": {
    "clientMachineId": null,
    "identityId": "410c8aa5-465c-42b8-a75b-56d40472da9f",
    "identityMasterRegion": "us-east-1",
    "contextId": "410c8aa5-465c-42b8-a75b-56d40472da9f",
    "contextMasterRegion": "us-east-1",
    "expiresAt": "2018-05-24T15:16:00.694Z",
    "componentId": "f9ab9f3f-175d-4006-b445-5d169a4c1f50",
    "componentScope": "*/*",
    "createdAt": "2018-05-23T15:16:00.701Z",
    "updatedAt": "2018-05-23T15:16:00.701Z",
    "masterRegion": "us-east-1",
    "id": "9d3ed28a-ad5a-4f56-bd1e-0c65102e3291"
  },
  id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}

Note that id_token is only present in response if it was initially requested as part of ROPC or CC authentication flows.

Supported media type of the representation:
application/hal+json”

NOTE: Client MUST NOT have any prior knowledge about URL in the links mentioned
above as well as rely on its presence all the time. All the client MUST understand
is that presence of a link enables discovery of the “current” token. Link is
optional and its absence means that respective resource is not available.

It is possible to log out via auth-token:removal link by sending DELETE request
to it. The request will look like:

DELETE /auth/tokens/current
...
Cookie: avidAccessToken=...

If operation is successful then 204 No Content status will be
returned in response.

Extending current token

It is possible to extend token expiration time, for this you should send POST
request to the link under the auth-token:extend rel. So, the request will look like:

POST /auth/tokens/current/extension
...
Cookie: avidAccessToken=...

The request body MUST be empty. The token will be extended as defined by the semantics
of IAMs mergeToken operation when no expiresAt and expiresIn is specified.

If extending token expiration is successful, then you will receive 200 OK status
with actual content of the token. Client MUST refresh / replace the token on it’s
side since access token MAY be rotated by the server.