What is Single Sign On
Single Sign On (SSO) is the process of utilizing a single set of login credentials across multiple systems. SSO is key to reducing the friction that users experience when navigating connected systems as well as improving a user’s online security.
When thinking about SSO, a system can be a Provider and/or a Consumer. A Provider is sometimes referred to as an Identity Server and is responsible for maintaining and authenticating the user’s credentials. A Consumer on the other hand is sometimes referred to as the Resource Server and contains the information that the user wants to interact with.
It is also important to understand the difference between Authentication and Authorization. Authentication is the means of proving a user is who they say they are, whereas Authorization is process of defining what the user is allowed to do. An Identity Server is just responsible for the Authentication and the Authorization is then left up to the individual Consumers.
What does GrowthZone support?
GrowthZone can act as either an SSO Provider or Consumer depending on the customer’s needs.
GrowthZone as a Provider
GrowthZone is an SSO Provider by acting as an OpenID Connect (OIDC) server. ODIC is an Authentication layer that is built upon the OAuth 2.0 protocol and provides consumers with a unique Identity Token in the format of a JSON Web Token (JWT) for each user.
Whilst ODIC is built upon the OAuth 2.0 protocol, it serves a different purpose from the default implementation of OAuth. OAuth is an open standard for access delegation whereby the Provider supplies an Access Token, also in the format of a JWT, to the Consumer which the Consumer can then use to gain access to the Providers resources. With ODIC, the Provider supplies an Identity Token which the Consumer can then use to identify, and optionally implicitly authenticate the user on their own system.
JSON Web Token
A JSON Web Token (JWT) is a standard for creating claims based tokens. A claim is a statement that the issuer makes about the user, for example, Email, First Name, Last Name, etc. are commonly presented claims about a user.
The tokens are signed by the issuer's private key and the public key can then be used by consumers of the token to ensure that the token has not been tampered with since it was issued. The claims that are included in the token are dependent on the implementation and also the purpose of what the token is being used for.
Identity Tokens and Access Tokens are therefore the same thing except that they differ in terms of the claims with each.
It is important to remember that the JWT is encoded and not encrypted. Given that it is encoded, it can then be decoded by anyone that is in possession of the token and the claims are therefore visible in plain text. For this reason, sensitive information should never be included as a claim.
To avoid an attack where the attacker modifies information in the JWT, the JWT is digitally signed using an asymmetric key (a key that has a public and a private component). The digital signature can be verified by the recipient using the public key supplied by the Provider and thus the Consumer can have a high level of confidence that the token has not be tampered with.
The public keys are shared via a JSON Web Key Set (JWKS) at the /openid/well-known/jwks endpoint.
GrowthZone as a Consumer
GrowthZone can act as a SSO Consumer through its support for SAML logins. SAML stands for Security Assertion Markup Language and is an XML based standard for exchanging authentication and authorization information between the Provider and the Consumer.
The workflow for a SAML login is initiated from the GrowthZone application either by the user clicking a SAML login button or via an automatic redirection, as can be the case for the Member Information Center.
When the SAML login is initiated, the user is redirected to the login page on the providers system where they enter their credentials. The credentials that they use here are the credentials that they would use if they were logging into the providers system directly, not credentials that they may have for GrowthZone. The provider is then responsible for authenticating the user, i.e., proving that they are who they say they are, and then redirecting them back to GrowthZone.
When the user is redirected back to GrowthZone, the provider passes a message which contains enough information for GrowthZone to identify that it is an existing contact or a new contact that needs to be created. Once the appropriate contact is found (it could have been newly created) the user is logged in as that contact.
As a Consumer, there must be a high level of trust in the Provider as GrowthZone will use that information in place of its own authentication mechanisms.
OpenID Connect
GrowthZone's implementation of OpenID Connect supports the following workflows. The full reference to the OpenID Connect specification can be found online.
response_type | Flow | Description | Security Concerns |
---|---|---|---|
code | Authorization Code Flow | Provides an Authorization Code that can be exchanged for an Access Token and optionally an Identity Token that can be used for delegated access. | Uses a back-channel on a separate endpoint to exchange the code for the tokens. This provides the highest level of security of all the flows. |
id_token | Implicit Flow | Provides an Identity Token which can be used for Single Sign On to a Consumer. | This is typically used for Single Page Applications (SPA) that have no means of securing a Client Secret and not recommended where other flows are possible. |
id_token token | Implicit Flow | Provides an Identity Token which can be used for Single Sign On to a Consumer along with an Access Token that can be used on the Provider. | This is typically used for Single Page Applications (SPA) that have no means of securing a Client Secret and not recommended where other flows are possible. |
code id_token | Hybrid Flow | Provides an Authorization Code as per the Authorization Code Flow and an Identity Token as per the Implict Flow. | Whilst the authorization code provides a higher level of security to gain an Access Token, the Identity Token still has the same security concerns as the Implicit Flow. |
code token | Hybrid Flow | Provides an Authorization Code as per the Authorization Code Flow and an Access Token as per the Implict Flow. | Whilst the authorization code provides a higher level of security to gain an Access Token, the Access Token that is returned in the response still has the same security concerns as the Implicit Flow. |
code id_token token | Hybrid Flow | Provides an Authorization Code as per the Authorization Code Flow and an Access Token and Identity Token as per the Implicit Flow. | Whilst the authorization code provides a higher level of security to gain an Access Token, the Access Token and Identity Token that are returned in the response still have the same security concerns as the Implicit Flow. |
Security Concerns
Whilst there are six different workflows, they can be grouped as either Authorization Code Flow or Implicit Flow when it comes to discussing their level of security. The Hybrid Flow is a combination of the Authorization Code Flow and the Implicit Flow but from a security perspective the Hybrid Flow has the same pit falls as the Implicit Flow.
Implicit Flow
The Implicit Flow was designed to allow token access to Single Page Applications (SPA) that had no backend servers and no means to secure a Client Secret. These requests are designed to operate from the clients Web Browser to the OpenID Connect server and therefore the request are visible to the end users.
To understand the difference, we can look at and example request and response for the id_token Implicit Flow.
GET /oauth/authorize?
response_type=id_token
&client_id=s6BhdRkqt3
&redirect_uri=https%3A%2F%2Facme.com%2Fopenid%2Fcallback
&scope=openid%20profile%20email
&nonce=n-0S6_WzA2Mj
&state=af0ifjsldkj HTTP/1.1
Host: example.com
The request to the authorization endpoint contains a visible client_id which makes it easy for an attacker to simulate their own identity requests using a stolen client_id. More reliable OpenID Connect servers will validate the redirect_url against a known whitelist of endpoints to reduce the likelihood of an attack, but this is still not foolproof.
HTTP/1.1 302 Found
Location:
https://acme.com/openid/callback?id_token=eyJraWQiOi...&state=af0ifjsldkj
The response to the authorization request includes the id_token and is visible to the user and thus a potential attacker. The severity of the attack with a stolen Identity Token is largely dependent on the implementation of the OpenID Connect server and the information that is included within the Identity Token, however the Identity Token by nature includes enough information for federated identification of a user and this is likely through exposing an email address or username.
GrowthZone will not accept the Identity Token to be used for any form of authentication or authorization so no further information about a user other that was is included in the Identity Token can be gained by an attacker. If the Identity Token has been obtained by a responsible 3rd party then they can use it in a token exchange to gain an Access Token which therefore enables delegated authentication to the GrowthZone API's. The token exchange process occurs in a similar manner to the Authorization Code Flow and therefore has a much reduced attack surface.
Authorization Code Flow
The major difference between the Authorization Code Flow and the Implicit Code Flow is that the Authorization Code Flow uses a separate endpoint to obtain its tokens and thus is designed to occur over a backchannel such that it is not exposed to the user. A backchannel is simply the term given to communication between two servers without any user involvement or visibility. The use of a backchannel also allows the request to be validated using the Client ID and Client Secret which should never be exposed to a user or attacker.
The following is an example request using the Authorization Code Flow.
GET /oauth/authorize?
response_type=code
&client_id=s6BhdRkqt3
&redirect_uri=https%3A%2F%2Facme.com%2Fopenid%2Fcallback
&scope=openid%20profile%20email
&nonce=n-0S6_WzA2Mj
&state=af0ifjsldkj HTTP/1.1
Host: example.com
The request contains the Client ID which therefore has the same risks as the Implicit Flow, however that is really the only cause for concern here.
HTTP/1.1 302 Found
Location:
https://acme.com/openid/callback?code=Qcb0Orv1zh...&state=af0ifjsldkj
Unlike the Implicit Flow that contains the tokens embedded in its response to the user, the response to an Authorization Code Flow request contains a code parameter. This code is an opaque value that is neither encoded nor encrypted and has no special meaning. The code's only purpose is to be used in exchange for the actual tokens.
To further reduce the potential attack surface, the code is a Short-Lived One-Time-Only code which means that it can only be used once and must be used within a short time frame of it being received by the client, usually around five minutes. Any attempt to use the same token more than once or after the expiry period will cause the exchange to fail.
Once the client receives the code, usually via means of a HTTP POST to the callback endpoint, it can be exchanged for the token through a separate token endpoint.
POST /oauth/token HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=s6BhdRkqt3
&client_secret=kd9jjk09ksqwhs8u3hjnd
&code=Qcb0oOrv1zh...
&redirect_uri=https%3A%2F%2Facme.com%2Fopenid%2Fcallback
The request to the token endpoint now includes the Client Secret for the first time, this is because the request here is occurring over a backchannel and not visible to the user and as such is safe to do so. The OpenID Connect server can now verify the Client ID against the Client Secret and also verify that the code was issued to the Client ID.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "SlAV82hkKG",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "eyJhbGciOi..."
}
The response to the token endpoint is a JSON object that contains the id_token and additionally an access_token.
In the case of GrowthZone, the Access Token can now be used in subsequent requests to the GrowthZone API to discover more information about the user.
GrowthZone Single Sign On Example
The following is an example of the OpenID Connect Workflow to enable SSO for a 3rd Party.
The is recommended workflow for GrowthZone is the Authorization Code Flow as it offers the highest level of security for all of the supported flows.
Pre-requisites
The Consumer must have a valid OAuth Client ID and Client Secret that have been issued by the GrowthZone application.
Step 1
The consumer redirects the user to GrowthZone.
The details of how and when this redirection occurs are specific to the implementation of the consumers application. As part of the redirection, the consumer will also include their specific OAuth Client ID in the request as a query string parameter. GrowthZone uses the OAuth Client ID to ensure that the consumer is valid and also has permissions to provide a login for a specific tenant.
An example authentication request would look like the following.
GET https://growthzoneapp.com/oauth/authorize?client_id=12345
&response_type=code
&response_mode=form_post
&redirect_uri=http://example.com/openid/callback
&scope=openid+profile+email
&state=jd8Udndha7d
&nonce=93kdjdf873jdnfbnyhsgbdk
The above example has had the URL encoding removed to assist in the clarity of the example.
Parameter | Necessity | Description |
---|---|---|
client_id | REQUIRED | The OAuth Client ID that was issued by the GrowthZone Application. |
response_type | REQUIRED | A value that indicates the type of workflow to be used. This example is using the more secure Authorization Code Flow. |
response_mode | OPTIONAL | The mode in which the Provider will send the code to the Consumer. If not specified, the default value of form_post will be used whereby the Provider will perform a HTTP POST to the redirect_uri passing the code as a form parameter. form_post is the recommended value for Web applications as it is more secure. |
redirect_uri | REQUIRED | The URI to Redirect to once the authentication has been successfully completed. |
scope | REQUIRED | The scopes that define what it is that the Consumer is wanting to do. To return an Identity Token the value must contain at lease openid and profile. |
state | RECOMMENDED | The state is used to protect the end user from Cross Site Request Forgery (CSRF) attacks. The state is generated by the Consumer and then included by the Provider when redirecting back to the callback URI. This allows the Consumer to match the original authorization request to the callback. |
nonce | RECOMMENDED | A string value that is used to associate a client session with the ID Token. If present, this value will be included in the ID Token that is returned by the Provider and the Consumer must then validate it is the same value that was included in the original authorization request. |
Step 2
The user performs a login to GrowthZone.
The user will log using their standard GrowthZone credentials. This login operation occurs entirely on the GrowthZone servers thus improving the security of the process.
The login process includes a second step (which can be disabled by an administrator) that presents the user with a grant screen. The grant screen notifies the user of the application that they are giving permission and prompts them to continue.
Step 3
The successful login is redirected back to the Consumer.
Once the login has succeeded, the Provider will execute a callback to the Consumer over the redirect_uri that was provided by the Consumer in the authorization request.
POST /openid/callback
HOST: example.com
Content-Type: application/x-www-form-urlencoded
code=kiuwyl091jkjndnh&state=jd8Udndha7d
The client must validate that the state value is the same was what was included in the authorization request and not proceed if there is a mismatch.
Step 4
The code is exchanged for an Identity Token.
POST /oauth/token HTTP/1.1
Host: growthzoneapp.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id=12345
&client_secret=54321
&code=Qcb0Orv1zh...
&redirect_uri=https%3A%2F%2Facme.com%2Fopenid%2Fcallback
The response contains the Identity Token along with an Access Token that can be used to request further information about the user.
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "eyJhbGciOi..."
}
Step 5
The consumer requests further information about the user.
A request can be sent to the userinfo endpoint to return further information about the user.
The request must be authorized using the access_token that was provided from the previous step. This access token is sent in the Authorization header as a Bearer token.
GET /oauth/userinfo HTTP/1.1
Host: growthzoneapp.com
Authorization: Bearer S1AV32hkKG
The response is a JSON object containing information about the user. The sub property is the internal GrowthZone identifier.
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub": "12345",
"given_name": "Jane",
"family_name": "Doe",
"email": [email protected]“,
MemTypes”: “[1,2,3]”
}