Table of Contents | ||
---|---|---|
|
Introduction
The OAuth 2.0 authorization framework enables a third-party application (the client) to obtain limited access to an HTTP service on behalf of a resource owner (the end user). For instance, a tool for generating advanced meta analysis graphs (the client) could obtain access to read and possibly modify a review author's (the end user's) reviews in Archie.
...
OAuth 2.0 is the framework to build authentication protocols, actual protocol used in Cochrane project is OpenID Connect (OIDC), which is implemented in Keycloak. For more information on OIDC implementation in Keycloak please go to: https://www.keycloak.org/docs/39.30/server_admin/topics/sso-protocols/index.html#_oidc.html.
OIDC itself uses JSON Web Token (JWT) standards to define identity token format and ways to digitally sign and encryt data. More information about JWT can be found at: https://tools.ietf.org/html/rfc7519.
...
OAuth 2.0 can also be used for single sign on, i.e. the ability for the end user to log into the client using his or her Cochrane Account, by utilising the authentication part of the OAuth flow only, and not using the access token for further API calls.
Anchor | ||||
---|---|---|---|---|
|
Before a client can use the system it must be registered in Keycloak with the following information (see Keycloack documentation for details):
- Client ID;
- Client name (for display);
- Client secret (password - for confidential clients only, see below) - generated by Keycloak;
- One or more redirect URIs (to which the end user's user agent (typically a browser) is redirected after completing the Cochrane Account authentication);A URI pointing to an image/logo that can be displayed on the login/consent page (note: currently not used on the Cochrane Account login page);
- A URI pointing to a landing page for the client. This is used on various Cochrane Account pages (except login) to return the user to the client;
- The resource scopes that the client should have access to (see below);
- A client type.
In addition, for the client credentials flow, an Keycloak user must be associated with the client (see documentation for details). The access token obtained with this flow will Keycloak automatically generates a user. It must be configured to be associated with this user and the user's permissionsan Archie user, which may grant it permissions in Archie.
There are two different types of client, confidential and public:
...
The registration of redirect URIs is vital for the security of the system, and Keycloak only allows complete URIs to be registered (see Keycloak documentation for details, part Valid Redirect URIs).
Anchor | ||
---|---|---|
|
...
All endpoints require a secure connection (HTTPS). If plain HTTP is used an error status code 403 (forbidden) is returned.
...
|
Environment | KEYCLOAK_BASEURL | ARCHIE_BASEURL |
---|---|---|
Test | https:// |
...
test- |
...
login.cochrane.org/ |
Primary endpoints
Authorization endpoint:
hostname/auth/realms/cochrane/protocol/openid-connect/auth
Token endpoint:
hostname/auth/realms/cochrane/protocol/openid-connect/token
Authorization Code Grant (server-side) flow
Step A: Authorization code grant
https://test-archie.cochrane.org/ | ||
Training | https://training-login.cochrane.org/ | https://training-archie.cochrane.org/ |
Production | https://login.cochrane.org/ | https://archie.cochrane.org/ |
All endpoints require a secure connection (HTTPS). If plain HTTP is used an error status code 403 (forbidden) is returned.
Primary endpoints
Authorization endpoint:
${KEYCLOAK_BASEURL}/auth/realms/cochrane/protocol/openid-connect/auth
Token endpoint:
${KEYCLOAK_BASEURL}/auth/realms/cochrane/protocol/openid-connect/token
Authorization Code Grant (server-side) flow
Step A: Authorization code grant
The server-side flow is The server-side flow is optimised for confidential clients, although in theory it could be use by a public client. The first step is for the client to obtain an authorization code from Keycloak.
...
state (highly recommended): An (often randomly) generated value used by the client to maintain state between the request and callback. The parameter will be returned unchanged in the callback.
Anchor scope_description scope_description
scope (optional): The scope of the resources that the access token should provide access to. Possible values are:
...
Multiple scopes can be provided as a space separated list, e.g. "person document", but "all" or "none" must stand alone. If no scope is specified, "none" is used (note: the default is not all scopes registered with the client). Only scopes registered with the client are allowed. access_type (optional, non-standard): Possible values are "online" (default) and "offline". If set to "offline" for a confidential client, a refresh token is issued together with the access token in step B.
At the authorization endpoint the end user will have to log into Cochrane Account if he or she is not logged in already. After the authentication, provided that the scope of the request is different from "none", the end user may be presented with a consent screen where he or she has to agree to give the client access to resources in Archie within the given scope before the flow may continue. Note: the consent screen is not shown for clients hosted on *.cochrane.org domains.
Once the authentication Once the authentication and possible consent is in order, i.e. the end user has authorized the client to access his or her data, the browser is redirected back to the redirect URI (using a HTTP 302 status code) with the following query parameters added:
...
state (optional): The state parameter provided by the client, if any.
session_state: A unique value that identifies the current user session.
In case of an error condition, depending on the error type, an error message will be displayed either directly to the end user (if there is a problem with the client ID or redirect URI), or it will be provided as an error parameter in the redirect URI to the client. For instance, if the end user does not give his or her consent, an "access_denied" error is returned to the client. See the specification section 4.1.2.1 for details.
Step B: Access token request
In the second step the client exchanges the authorization code with an access token server-to-server without involving the user's browser, and it requires client credentials as Basic auth.
The client's server makes a POST request to the token endpoint with the following parameters included ("application/x-www-form-urlencoded" format):
...
redirect_uri (optional): Required if the redirect_uri parameter was provided in step A, in which case the parameter must have the exact same value. This parameter is not used for redirecting at this step, only for added security.
Client authentication
For any call to the token endpoint, including this, the client must provide its credentials as Basic HTTP authentication with client ID as user name and client secret as password. The authentication must be provided in the Authorization header for a confidential client.
Before building the header for Basic authentication, client IDs and client secrets containing special characters including %, +, and all non-ASCII characters must be encoded using the UTF-8 character encoding scheme first; the resulting octet sequence then needs to be further application/x-www-form-urlencoded (see section 2.3.1 of the specification). To build the header, the encoded credentials are separated by a colon, the resulting string is base 64 encoded, and the result is prefixed with "Basic ".
Access code response
The response to the POST request is a JSON object (MIME type "application/json") like this:
{
"access_token": "3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc"<JWT>,
"tokenexpires_typein":300,
"Bearer" "refresh_expires_in":1800,
"refresh_token":<REFRESH_TOKEN>,
"expirestoken_intype":"bearer",
"14400"not-before-policy":1549438553,
"refreshsession_tokenstate": "fa5e931aa4cecfcf4ae3bebd9648aa14942d6c79f6c26ffa3bd44d1e106522bc"
}"db478a1b-3acd-4737-bcce-bf83bc0d9eda",
"scope":"person email"
}
The first The first field is the actual access token which should be kept on the server and never passed to the end user's browser. The token_type will always be "Bearer" in this implementation. The number of seconds that the token is valid is given in expires_in. A refresh_token field is only returned if the access_type in step A is set to "offline".
Error response
In case of an error condition Keycloak responds with a HTTP 400 status code with the error message included in the body of the response as a JSON object, e.g.:
...
For further details see section 5.2 of the specification.
Implicit Grant (client-side) flow
The client-side flow is optimised for public clients such as pure HTML5/JavaScript clients.
...
state (highly recommended): A (often randomly) generated value used by the client to maintain state between the request and callback. The parameter will be returned unchanged in the callback.
scope (optional) : The scope of the resources that the access token should provide access to. Possible values are:
"none": The default, single sign on only, no access to data
"person: Manage person records
- "group": Manage groups
"document": Manage documents and reviews
- "workflow": Manage tasks and workflows
"crs": Manage studies in CRS
"crso": Manage studies in CRSO
"all": Manage any resource type
(the same as in scope description above)
Multiple scopes can be provided as a space separated list, e.g. "person document", but "all" or "none" must stand alone. If no scope is specified, "none" is used (note: the Multiple scopes can be provided as a space separated list, e.g. "person document", but "all" or "none" must stand alone. If no scope is specified, "none" is used (note: the default is not all scopes registered with the client). Only scopes registered with the client are allowed.
...
In case of an error condition, depending on the error type, an error message will either be displayed directly to the end user (if there is a problem with the client ID or redirect URI), or it will be provided as an error parameter in the fragment component of the redirect URI to the client. See the specification section 4.2.2.1 for details.
The format of the access token and error response objects is as described above for the Authorization Code Grant. However, no refresh token is supplied.
Anchor | ||||
---|---|---|---|---|
|
The Resource Owner Password Credentials flow allows client to obtain an access token using resource owner's credentials (username and password). The Password grant is one of the simplest OAuth grants and involves only one step: client uses resource owner's username and password to make POST request to the server to exchange password for access token. This authorization grant type requires that the application collect the user’s password.
...
client_id(required): Must be set to identifier of the client that was obtained during client registration.
scope (optional): The scope of the resources that the access token should provide access to. Possible values are:
"none": The default, single sign on only, no access to data
"person: Manage person records
- "group": Manage groups
"document": Manage documents and reviews
- "workflow": Manage tasks and workflows
"crs": Manage studies in CRS
"crso": Manage studies in CRSO
- "linked_data": Access to linked data resources
"all": Manage any resource type
Access Code Response
The response to the POST request is a JSON object (MIME type "application/json") like this:
{
"access_token": "3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc",
"token_type": "Bearer",
"expires_in": "14400"
}
Client Credentials Grant flow
The client credentials flow allows a confidential client (server) to obtain an access token server-to-server without involving an end user. Before this flow is used, an Keycloak user must be associated with the client. The access token obtained with this flow will be associated with the user and the user's permissions.
) (the same in scope description above).
The format of the access token and error response objects is as described above for the Authorization Code Grant.
Client Credentials Grant flow
The client credentials flow allows a confidential client (server) to obtain an access token server-to-server without involving an end user. Before this flow is used, an Keycloak user must be associated with the client. The access token obtained with this flow will be associated with the user and the user's permissions.
The server makes a POST request to the token endpoint with the following parameters included ("application/x-www-form-urlencoded" format):
grant_type (required): Must be set to "client_credentials" for this flow.
scope (optional) (the same as in scope description above).
Multiple scopes can be provided as a space separated list, e.g. "person document", but "all" or "none" must stand alone. If no scope is specified, "none" is used (note: the default is not all scopes registered with the client). Only scopes registered with the client are allowed.
Authentication must be provided in the Authorization header as described for the Authorization Code Grant under client authentication.
The format of the access token and error response objects is as described above for the Authorization Code Grant.
Anchor | ||||
---|---|---|---|---|
|
Keycloack provides a mechanism for validating access token. There are two ways to approach it - using Introspection Endpoint and validating tokens locally. See Keycloack documentation for more details.
Using Introspection Endpoint
In order to manually validate a token issued by Keycloack, invoke Introspection Endpoint /realms/{realm-name}/protocol/openid-connect/token/introspect
Please note that this endpoint can only be invoked by confidential clients (see OAuth 2 documentation for details).
To validate a token, make a POST request with parameters sent as The server makes a POST request to the token endpoint with the following parameters included ( "application/x-www-form-urlencoded" format):
grant_type (required): Must be set to "client_credentials" for this flow.
scope (optional): The scope of the resources that the access token should provide access to. Possible values are:
"none": The default, single sign on only, no access to data
"person: Manage person records
- "group": Manage groups
"document": Manage documents and reviews
- "workflow": Manage tasks and workflows
"crs": Manage studies in CRS
"crso": Manage studies in CRSO
- "linked_data": Access to linked data resources
"all": Manage any resource type
Multiple scopes can be provided as a space separated list, e.g. "person document", but "all" or "none" must stand alone. If no scope is specified, "none" is used (note: the default is not all scopes registered with the client). Only scopes registered with the client are allowed.
Authentication must be provided in the Authorization header as described for the Authorization Code Grant under client authentication.
Access code response
The response to the POST request is a JSON object (MIME type "application/json") like this:
{
"access_token": "3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc",
"token_type": "Bearer",
"expires_in": "14400"
}
The access_token field contains the actual access token. The token_type will always be "Bearer" in Keycloak implementation. The number of seconds that the token is valid is given in expires_in.
Error response
In case of an error condition Keycloak responds with a HTTP 400 status code with the error message included in the body of the response as a JSON object, e.g.:
{
"error": "invalid_grant",
"error_description": "Invalid authorization code."
}
For further details see section 5.2 of the specification.
https://www.keycloak.org/docs/latest/securing_apps/index.html#validating-access-tokens
Validating the access token
Keycloack provides a mechanism for validating access token. There are two ways to approach it - using Introspection Endpoint and validating tokens locally. See Keycloack documentation for more details.
Using Introspection Endpoint
In order to manually validate a token issued by Keycloack, invoke Introspection Endpoint /realms/{realm-name}/protocol/openid-connect/token/introspect
Please note that this endpoint can only be invoked by confidential clients (see OAuth 2 documentation for details).
To validate a token, make a POST request with parameters sent as "application/x-www-form-urlencoded" data to the Introspection Endpoint (see above) with the following query parameters included:
introspection. Might take 2 possible values: access_token or refresh_token.
data to the Introspection Endpoint (see above) with the following query parameters included:
introspection: takes one of two possible values: access_token or refresh_token.
The response is a JSON object (MIME type "application/json") like this:
{
"active": true,
"scope": "document",
"client_id": "l238j323ds-23ij4",
"user_name": "jdoe",
"token_type": "bearer"
"exp": 1419356238,
"iat": 1419356238,
"nbf": 1419350238
}
Local validation
Due to the fact that Keycloack issued tokens are JWT digitally signed and encoded using JWS, access token can be locally validated using public key of the issueing realm. The realm’s public key can be be looked-up and cached using the certificate endpoint with the Key ID (KID) embedded within the JWS.
Certificate endpoint is: /realms/{realm-name}/protocol/openid-connect/certs. It returns public keys enabled by realm encoded as JSON Web Key (JWK).
Accurate clock synchronisation is required for local token validation, so it should not be used in environments where this can't be guaranteed (e.g. end-user web browser).
Refreshing an access token
Refresh tokens can be used to obtain a new access token without asking for the end user's consent. Access tokens expire after 4 hours unless they are revoked. Refresh tokens are valid for 180 days or until they are used to obtain a new access token.
To refresh an access token (server-side flow only) the client's server makes a POST request to the token endpoint with the following parameters included ("application/x-www-form-urlencoded" format):
grant_type (required): Must be set to "refresh_token".
refresh_token (required): The previously obtained refresh token.
The format of the response is identical to the one returned when the original access token was obtained.
Note that a new refresh token is also issued with the response, so the old refresh token must be discarded by the client and replaced by the new token.
In case of an error condition, e.g. if the refresh token is no longer valid, the response will follow the same structure as for other token endpoint errors. The client should discard the refresh token and obtain a new access token using the server-side flow.
Anchor | ||||
---|---|---|---|---|
|
Keycloak provides a possibility to configure different lifespan for access token and refresh token. This is all done on the Tokens tab in the Keyclock. Expiration time can be configured for:
- Access token in Access Token Lifespan field
- Refresh token in SSO Session Idle field
See Session and Token Timeouts for more detailed information.
Using an access token to access the Archie API
The access token must be passed to the API in the Authorization header, e.g.:
GET ${ARCHIE_BASEURL}/rest/reviews/CD000004 HTTP/1.1
Authorization: Bearer 3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc
The result, if the call succeeds, is the review in XML format (MIME type "application/xml"). If the call fails an HTTP error status code will be returned.
See Review Document API for more examples.
Checking permissions for a user
The access token must be passed to the API in the Authorization header, e.g.:
GET ${ARCHIE_BASEURL}/oauth2/permissions HTTP/1.1
Authorization: Bearer 3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc
The response The response is a JSON object (MIME type "application/json") like this:
...
[ {
...
...
"
...
resource": "
...
folder",
...
"
...
entity": "
...
all",
...
"
...
grants": ["
...
delete",
...
...
"view_folder", "read_files", "write"] } { "
...
resource":
...
"person",
...
"
...
entity":
...
"all",
...
"
...
grants":
...
}
Local validation
Due to the fact that Keycloack issued tokens are JWT digitally signed and encoded using JWS, access token can be locally validated using public keyof the issueing realm. The realm’s public key can be either hard coded in validation code, or the public key culd be looked-up and cached using the certificate endpoint with the Key ID (KID) embedded within the JWS.
Refreshing an access token
Refresh tokens can be used to obtain a new access token without asking for the end user's consent. Access tokens expire after 4 hours unless they are revoked. Refresh tokens are valid for 180 days or until they are used to obtain a new access token.
To refresh an access token (server-side flow only) the client's server makes a POST request to the token endpoint with the following parameters included ("application/x-www-form-urlencoded" format):
grant_type (required): Must be set to "refresh_token".
refresh_token (required): The previously obtained refresh token.
The format of the response is identical to the one returned when the original access token was obtained:
{
"access_token": "71c9e4fba2f43815719b82132f3104ee71930031f50087049e3beb357d9a5107",
"token_type": "Bearer",
"expires_in": "14400",
"refresh_token": "abad3cdea83b2e4d28594db35b526689da1e295fb34dcb13668723f1ecac14e7"
}
Note that a new refresh token is also issued with the response, so the old refresh token must be discarded by the client and replaced by the new token.
In case of an error condition, e.g. if the refresh token is no longer valid, the response will follow the same structure as for other token endpoint errors. The client should discard the refresh token and obtain a new access token using the server-side flow.
Using an access token to access the API
The access token must be passed to the API in the Authorization header, e.g.:
GET hostname/auth/realms/master/protocol/openid-connect/token HTTP/1.1
Authorization: Bearer 3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc
The result, if the call succeeds, is the review in XML format (MIME type "application/xml"). If the call fails an HTTP error status code will be returned.
...
["view", "view_private", "write", "delete", "mark_duplicate", "select_topics", "affiliation_crud"]
}
]
Checking permissions for a single review
As another example, the API contains a method to get the user's permissions for a single review:
GET TO BE IMPLEMENTED WITH:
${ARCHIE_BASEURL}/rest/reviews/CD000004/permissions HTTP/1.1 Jira server JIRA serverId 84faf8a3-76e6-3cf7-862e-a59f68644f2d key CIT-30
Authorization: Bearer 3e8ec1a3d43c983b57df0616b498c04807b466e919999aa0f3f3aabca1dd48cc
...
If the call fails an HTTP error status code will be returned.
...