As mentioned in Solution Overview the i-refactory server requires that each request contains an OAuth2 compliant token.
In this document we will explain the OAuth2 token and the i-refactory OAuth2 server.
When a request to our i-refactory server is created you need to include an OAuth2 compliant token.
The access token issued is a JWT token which content follows the OAuth 2.0 Token Introspection specification. It includes the following properties:
iss
: the url of the authorization server that issued the token.iat
: the time the token was issued.exp
: the time the token will expire.aud
: the recipients the token is intended for. Currently this is the client that requested the token.sub
: the machine readable identifier of the user (or client) who granted access.username
: the human readable identifier of the user (or client) who granted access.client_id
: the client that requested the token.We have added two additional properties:
roles
: the roles of the user (or client) who granted access.email
: the email address of the user.The roles
property is included because our i-refactory server will check if a client has the required role assigned to execute the API request. The email
property is included for backwards compatibility because in prior releases the email
property might have been used as the modifier
.
When you create an HTTP request you should add the JWT token to an authorization header. The content of the authorization header should start with the literal string Bearer
followed by a space followed by the JWT token.
This is an example of a call to our i-refactory server:
/**
* Create a tenant.
*
* @param {string} accessToken - the accessToken
* @param {Array} tenant - the tenant(s) to create
* @returns {Promise} - A promise.
*/
async function createTenant(accessToken, tenants) {
const url = `${IREFACTORY_SERVER}/v1/acmDatadel/tenant`,
headers = {
Authorization: `Bearer ${accessToken}`
},
data = tenants.map(tenant => {
return {
code: tenant.code,
name: tenant.name,
description: tenant.description
};
}),
options = {
method: 'post',
url: url,
headers: headers,
body: data,
json: true
};
return httpRequest.post(options);
}
When the i-refactory server handles the request we will validate if the token is not tampered with and is not expired.
If the token is valid we will proceed with the request and check if the required role for the given API call is available in the token. If the required role is missing you are not authorized and you will receive an error.
We currently have the following roles which you can grant to a user or client application: DataViewer
,SystemManager
,DataManager
,DataOperator
,Developer
.
In our Swagger Specifications you can check which role is required for a given API call.
The i-refactory OAuth2 server implements the OAuth 2.0 framework. For the most part the protocol is followed as described in RFC 6749.
To learn more about OAuth2 this video does a good job explaining OAuth2:
Our i-refactory OAuth2 server supports the following OAuth2 flows. We will explain how these different types of flows work with an example.
Authorization code
In an authorization code
flow a client application will ask the OAuth2 server to serve a login page with which a user—by entering his credentials—can be authenticated and authorized.
sequenceDiagram
autonumber
participant A AS i-refactory Web App
participant B AS OAuth2 Server
participant C AS OAuth2 Login
A->>B: authorize request
Note right of A: "code"
B->>C: enter user credentials
C->>B: user credentials
B->>A: Authorization code
A->>B: token request
Note right of A: "authorization_code"
B->>A: access token(refresh token)
The sequence diagram example illustrates the steps and flow of a web application for which a user should be authorized. The flow of events (happy flow) is given. We will explain what happens in each step hereafter.
Step 1: Authorize request
The i-refactory web app discovers that a user has not yet been authorized. It sends an authorization request to the OAuth2 server. In this request the applications send its clientId, a random string and the redirect URI.
The OAuth2 server checks in the list of known clients if the clientId exists. It checks as well if the redirect URI in the request matches the list of registered redirect URI's for the clientId.
The random string is explained in step 5.
{note} An application should be registered in OAuth2 as a client which is allowed to make requests. The clientId is a unique identifier for each client. For clients which are allowed to make an authorization request the redirect URI's should be registered as well. The latter because the OAuth2 server needs to know to whom the response should be sent and as an additional check.
Step 2: Serve a user credentials page in the browser.
If all is well the OAuth2 server serves a login page in the browser. The user should enter its username and password. The login page receives a session id that is generated and stored in the OAuth2 server with a time to live of 60 minutes. The entered user credentials and the sessionId are sent to the OAuth2 server.
Step 3: Validate the user credentials
The OAuth2 server checks if the sessionId still exists (expires in 60 minutes) and if the user credentials are known in the list of users.
Step 4: Authorization code
The OAuth2 server sends an authorization code and the random string to the client application. This authorization code has a time to live of 30 seconds.
Step 5: Token request
The client application checks if the received authorization code was intended for it. The random string is used for this purpose. It should match with the request from step 1. The client application now sends a token request. In this request the clientId, clientSecret and accessToken are sent to the OAuth2 server with a grant type authorization_code
.
Step 6: Access Token
The authorization server checks if the authorization code still exists. It will then check the clientId and clientSecret. Finally, it will send a JWT token with the id of the user and the roles assigned to the user. The time to live of the access token is by default set to 3600 seconds. This is reflected in the exp
property of the token. The expiration lifetime can be configured. When the token has expired the token is no longer valid and the complete flow from step 1 should be restarted to get a new token.
If the client application has been granted the refresh token
privilege a refresh token is sent to the client application as well. A refresh token
has a time to live of 14 days. A client application can request a new access token with the refresh token
. This request is a refresh token
request.
The web application now has a valid token. The token contains the roles assigned to the user. The roles are used to enable or disable behaviour for which the user has access rights. The token also enables the web application to send requests to the i-refactory server.
{note} The i-refactory web application uses the
authorization code
flow to get a valid token. If an OAuth2 service other then the i-refactory OAuth2 server is used, then it should support at least theauthorization code
flow.
Client credentials
In a client credentials
flow a client requests a token by passing the clientId and clientSecret directly. The OAuth2 server does not serve a login page to enter credentials.
sequenceDiagram
autonumber
participant A AS Client
participant B AS OAuth2 Server
A->>B: token request
Note right of A: "client_credentials"
B->>A: access token
Step 1: token request
A client who has been granted client credentials
sends a token request to the OAuth2 server. In this request the client sends his clientId and clientSecret with a grant type client credentials
.
Step 2: access token
The OAuth2 server checks if the clientId and clientSecret are valid. It sends an access token
to the client with the client id and the roles assigned to the client.
The client now has a valid token
with which he can send requests to the i-refactory server. If the token
has expired the client should send a new token request
to the OAuth2 server.
{info} A
client credentials
flow never sends a refresh token.
Password
In a password
flow a client requests a token by passing the clientId, clientSecret, userId and user password directly. The OAuth2 server does not serve a login page to enter credentials.
sequenceDiagram
autonumber
participant A AS Client
participant B AS OAuth2 Server
A->>B: token request
Note right of A: "password"
B->>A: access token
Step 1: token request
A client who has been granted passwords
sends a token request to the OAuth2 server. In this request the client sends his clientId, clientSecret, user id and user password with a grant type password
.
Step 2: access token
The OAuth2 server checks if the clientId, clientSecret, user id and user password are valid. It sends an access token
to the client with the user as identifier and the roles granted to the user.
If the client application has been granted the refresh token
privilege a refresh token is sent to the client application as well. A refresh token
has a time to live of 14 days. A client application can request a new access token with the refresh token
. This request is a refresh token
request.
The client now has a valid token
with which he can send requests to the i-refactory server. If the token
has expired the client should send a new token request
to the OAuth2 server. If the client has received a refresh token
it could use a refresh token request
to get a new access token
.
Refresh token
In a refresh token
flow a client has received a refresh token
as part of the authorization code
flow. With this refresh token
a client can renew the access token
without asking the user to enter his user credentials.
sequenceDiagram
autonumber
participant A AS i-refactory Web App
participant B AS OAuth2 Server
A->>B: token request
Note right of A: "refresh_token"
B->>A: access token
Step 1: Token request
The client application sends a token request to the OAuth2 server. In this request the clientId, clientSecret and refreshToken are sent to the OAuth2 server with a grant type refresh_token
.
Step 2: Access Token
The authorization server checks the clientId, clientSecret and refresh token. A new access token is created together with a new refresh token. The id of the user and the roles of the users are returned in the access token. Both the access token and refresh token are sent to the client.
The client application now has a new access token and a new refresh token. As long as the OAuth2 server is not shutdown and as long as the refresh token has not expired a client application can indefinitely renew the access token.
The i-refactory OAuth2 server needs to be configured. Clients and users should be registered otherwise no one has access to the i-refactory server. In the i-refactory configuration file, an optional authorizationServer
section is added where you should configure the i-refactory OAuth2 server. You can read more here.
In this section we will explain the OAuth2 configuration in detail.
These are the general configuration parameters for the i-refactory OAuth2 server.
{
privateKey: {
doc: 'Location and name of the private key to use for encrypting access and OpenID tokens',
format: 'String',
default: 'missing configuration option: privateKey',
},
publicKey: {
doc: 'Location and name of the public key to use for decrypting access and OpenID tokens',
format: 'String',
default: 'missing configuration option: publicKey',
},
tokenExpiryTime: {
doc: 'The default lifetime in seconds of access and OpenID tokens. Used when no lifetime is configured for the client to which a token is issued.',
format: 'int',
default: 3600,
},
https: {
host: {
doc: 'The hostname for the server',
format: 'String',
default: 'localhost',
},
port: {
doc: 'The port number to which the server should listen',
format: 'int',
default: 3003,
},
key: {
doc: 'The private key to decrypt incoming messages',
format: 'String',
default: 'missing configuration option: https.key',
},
cert: {
doc: 'The certificate file which is used to establish the SSL connection',
format: 'String',
default: 'missing configuration option: https.cert',
},
}
}
The clients
section in the configuration contains a list of clients.
A client is an application that is granted access. For example the i-refactory web application or your own .Net or Java application.
Each client should be identified with a unique clientId and should have a clientSecret (an empty string is considered a valid clientSecret). A client can have grants which are the typical OAuth2 flows. A client can be granted roles which allows the client to make calls to the i-refactory server. For each client you have the option to overrule the expire time of the access token.
{info} If a client has been granted
authorization_code
you should register a redirectUri as well. The OAuth2 server needs to know to which URI to redirect when the user credentials are valid.
{
clients: {
doc: 'The third-party applications that may request access to the resources owned by the users',
format: 'irOauthArray',
default: [],
values: {
clientId: {
doc: 'The id of the third-party application',
format: 'String',
default: null,
},
clientSecret: {
doc: 'The password for the client. If the client is granted for client credential authentication, then a password is required.',
format: '*',
default: undefined,
},
redirectUri: {
doc: 'The redirection endpoints used by the authorization server to return responses containing authorization credentials to the client',
format: 'Array',
default: [],
},
grants: {
doc: 'The grant types the client is authorized for',
format: 'irOauthRestrictedArray',
allowedValues: ['authorization_code', 'client_credentials', 'password', 'refresh_token'],
default: [],
},
roles: {
doc: 'The roles the client is authorized for. Used when the client is granted access using it\'s credentials',
format: 'Array',
default: [],
},
tokenExpiryTime: {
doc: 'The lifetime in seconds of access and OpenID tokens issued to the client. Overrules the default lifetime for the server.',
format: 'int',
default: undefined,
},
},
}
}
The users
section in the configuration contains a list of users who are granted access. Users should be specified when clients have been granted authorization_code
or password
. In these flows it is the user who is granted access and not the client application.
{
users: {
doc: 'An array with users which should have an id, username, password, email and their roles',
format: 'irOauthArray',
default: [],
values: {
id: {
doc: 'The machine readable identifier of the user',
format: 'String',
default: null,
},
username: {
doc: 'The human readable identifier of the user',
format: 'String',
default: null,
},
password: {
doc: 'The username of the user',
format: 'String',
default: null,
},
email: {
doc: 'The email address of the user',
format: 'String',
default: undefined,
},
roles: {
doc: 'The roles the user is authorized for',
format: 'Array',
default: [],
},
},
}
}
We have made the following exceptions and implementation decisions in our i-refactory OAuth2 server:
Implicit flow
The implicit flow is not supported.
Scopes
According to the RFC a client can specify the scope of the access requested using the "scope" request parameter. If the issued access token scope is different from the one requested by the client, the authorization server must include the "scope" response parameter to inform the client of the actual scope granted.
This implementation ignores scopes. It will always issue an access token with the client who requested an access token, the user who granted access and the roles of the user who granted access.
Audience
The audience in the token could be used to identify the resource server for which the access token is intended.
This is not implemented: the i-refactory server does not check if the access token is intended for the server.
Authorize request handling
In an authorize request the client secret is not required. In the handling of an authorization request only the clientId and the redirect uri are validated. When valid a login screen is presented to the user where he enters a username and password. The username/password are sent to the authorization server. If the username and password are correct (the user is a valid user) the authorization server sends an authorization code to the client application. The client application is then able to request an access token. The authorization code's time to live is 30 seconds.
Token request handling
For the token endpoint a client should authenticate using its client id and client secret. This can be done by Basic authentication or by passing the id and secret in the body of the request.
Redirect uri
A redirect uri is required when starting an authorization code flow. The redirect uri will not be defaulted by the authorization server. But when the authorization code is traded for an access token we ignore any redirect uri, although according to OAuth 2.0 a redirect uri is required when a redirect uri is passed on in the authorization request (see section 4.1.3. Access Token Request)
Token invalidation
Tokens expire. It is not possible to invalidate a token. The i-refactory server only checks if a token has not expired and does not check the authorization server if the token is still valid or not.
If an authorization code is used more than once or is used by another client then the tokens issued based on that authorization code are NOT revoked as recommended in section 4.1.2. Authorization Response
Request parameters
If a request includes an invalid parameter or includes a parameter more than once, the request should fail with an invalid request error (see section 4.1.2.1. Error Response).
We do not check for invalid parameters or parameters included more than once.