Adding a new OAuth authorization provider
Application registration on the authorization server
xref:api:rest/v1/model/oauth/index.adoc
Recommendation. Before connecting a new specific OAuth authorization provider, it is useful to practice and master the process by connecting Yandex.ID
The application (communication system) must be registered with the authorization provider and identifiers must be obtained, which will be used in the future when communicating between the application and the authorization server. Usually, as part of the application, among other data it is necessary to pass 'redirect_uri' and 'scope'.
redirect_uri
'redirect_uri' - URL or a list of several URLs to which redirects are allowed after successful authorization. You should specify "https://<URL_DOMAIN>/oauth/receiver", or simply "https://<URL_DOMAIN>". Note that not all ISPs allow redirects to unencrypted http, but some do. The URL_DOMAIN should be the DNS name or address that all users use to get into the system without exception. This can be either a public name or a name known to the intranet DNS. In this case, you must specify "https://<URL_DOMAIN>/oauth/receiver" in the entity of the authorization provider URL. It is mandatory that at least one of the URLs passed for registration matches or is prefixed by this one.
{ ... "redirect_uri": "https://pbx.era-platform.ru/oauth/receiver", ... }
scope
'scope' - required amount of information about the user. It is necessary to find out the composition of parameters provided by the authorization server, and choose the list that corresponds to the most minimal authorization. Each authorization provider has a unique composition of options. Having selected the necessary values, you should set them also in the entity of the authorization provider. For example, for Yandex.ID it is:
{ ... "scope": [ "login:info", "login:email" ], "optional_scope": [], ... }
The main thing when selecting a scope is that the authorization service should provide a unique login. By the login, the communication system associates authorization with the user’s own account or can create this user at all. If there is no explicit login field, then it has to be uniquely created from what is provided. To do this, it will be necessary to create and use a service script for authorization by token, set in the parameters of the master domain (iam_token_svcscript_code), and in it select values from the information provided by the authorization server and form a unique login, assign it to the scenario variable "login".
Other options
If there are many domains and the authorization server distinguishes domain orientation within the communication platform, then it is necessary to return either the domain name or some other parameter that can be used to uniquely identify the domain of the communication platform to which this user is bound. If not, then the 'default_domain' property must be specified in the authorization provider entity. Implicit domain definition should also be performed in the token-based authorization service script (iam_token_svcscript_code), assign it to the script variable "domain".
{ ... "default_domain": "users.pbx.era-platform.ru", ... }
Of the non-standard query options, you may need to specify that the access_token be passed in JWT format. Some providers do this by default and some only optionally.
Filling the provider entity
Based on the results of registration, an entity of authorization provider is created (/rest/v1/model/oauth/Providers). At this stage the properties of the authorization provider entity are filled in 'client_id', 'client_secret', 'default_domain', 'scope', 'optional_scope'.
Options for mapping to a platform user account
Next, the authorization provider entity should specify which fields in the rest-server response of the authorization system correspond first to the user login, second to the user name, third to the domain, and fourth to other fields (email, etc.). Some other values can also be applied to the user entity (when creating or updating the user account). For example, for Yandex.ID in the entity of the authorization provider is set:
{ ... "query_login": [ "login" ], "query_name": [ "name", "real_name" ], "query_email": [ "email", "emails/0" ], "query_domain": null, "query_id": null, "query_info": null, ... }
Upon receiving the rest-server response according to the specified rules (fields 'query_*'), the corresponding value will be parsed and matched. The results go to the authorization request entity, and from there either directly to the matching mechanism or to the token authorization script, where they can be further transformed.
Basic properties of the provider
You should also specify basic parameters in the provider entity (their description is available in the help at https://vendor.era-platform.ru/docs/era/latest/api/rest/v1/model/endpoints/oauth/providers.html). For example, for Yandex.ID it is:
{ ... "id": "0c04b29b-0184-31f2-2e5e-3cecef28bebf", "key": "yandex", "label": "Login with Yandex ID", "enabled": true, "order": null, "icon_uri": "/.well-known/oauth/icons/ya.png", "dialect": "oauth", ... }
The ya.png icon should be manually placed in the ':SYNC/common/www/.well-known/oauth/icons' directory and the path to it should be specified in the property 'icon_uri'.
Other entity properties are discussed further in the context of the multi-step authorization process.
Authorization process
1. Redirect to the authorization server
If authorization providers are enabled, the corresponding buttons appear in the login form. The user can choose standard authorization or go through an external authorization provider. If it is necessary to direct all users to an external authorization provider, you should block the possibility of authorization via login-password. This can be done, for example, by activating the external authorization script in the master domain (iam_general_svcscript_code), in which the "result" variable should be set to 0 (authorization is prohibited).
First, according to the procedure, clicking on the external authorization button redirects to the authorization server specified in 'uri_authorize'. For Yandex.ID it is:
{ ... "uri_authorize": "https://oauth.yandex.ru/authorize", ... }
When redirecting to an authorization server, the following standard OAuth parameters are substituted into the URL and passed:
response_type = code client_id = <Account's client_id> scope = <Account's scope> optional_scope = <Account's optional_scope> redirect_uri = <Account's redirect_uri> state = <Unique request identifier>
Response_type = code - a constant defining the standard path - is substituted into the parameters: the authorization server will generate a code and pass it as an additional parameter during further forwarding, and the token will be received by the code.
The 'state' parameter from the redirect URL is generated by the platform for a specific external authorization request and is used to map different parts of the process for redirects. Depending on the 'state_mode' parameter from the entity, the 'state' parameter from the URL can be substituted either just as another parameter or included as a parameter specifically in the redirect_uri. This depends on whether the authorization server automatically rolls the 'state' parameter when redirecting or not, and whether it allows you to specify 'redirect_uri' with an extension relative to the one specified during registration, or requires a strict match between the two. For example, for Yandex.ID:
{ ... "state_mode": "param" ... }
If necessary, you should set additional parameters to customize a specific authorization provider. They will be substituted in the redirect URL without changes. For example, for Yandex.ID:
{ ... "params_authorize": { "display": "popup", "force_confirm": "yes" }, ... }
In this step, control of the authorization process is passed to the authorization server, and a request entity is added to the /rest/v1/model/oauth/Requests collection in state 'initial'.
2. Authorization on the external server OAuth
The user’s web page displays the authorization window of the connected OAuth server, where the user is prompted to enter a username and password and possibly agree to provide the application with certain information about the user (the same scope). If successful, the authorization server redirects back to the application page specified in the 'redirect_uri'.
If the user has been previously authorized and still has an active session on the authorization server, this step goes unnoticed and the authorization server immediately performs a redirect.
3. Reverse redirection after authorization, receiving access token
The control is passed back to the page by the above mentioned 'redirect_uri'.
Accordingly, the user’s web page again accesses the communication platform’s web server (this is /oauth/receiver). The web server, processing the received request with the passed parameter 'state' (identifier of a specific authorization request in the communication platform) and 'code' (identifier of the authorization session on the authorization server), contacts the authorization server for an access token and passes among the POST request parameters the value of 'client_secret'.
Since this is a server-side request, the OAuth protocol eliminates the possibility of client_secret being intercepted externally by a particular user. (By the way, this is where ESIA added an additional complication, GOST certificates are used, parameters are formed in a special way)
The access token is addressed to the authorization server at the address specified in the 'uri_token' property. for example, for Yandex.ID it is:
{ ... "uri_token": "https://oauth.yandex.ru/token", ... }
POST-the request is made with the following parameters
grant_type = authorization_code code = <Authorization code from redirected URL> client_id = <Account's client_id> client_secret = <Account's client_secret> redirect_uri = <Account's redirect_uri
In the parameters grant_type = authorization_code is substituted - constant, this is a standard mechanism (and the platform in the current version supports only it).
The authorization server in case of success returns 200 and in the body of the response JSON with the access token.
4. Retrieving user information
The web-server of the communication platform makes a GET-request to the REST-server of the authorization system, substitutes there a header
Authorization: OAuth <Access Token>.
The request is made to the address specified in the property 'uri_info':
{ ... "uri_info": "https://login.yandex.ru/info?format=json", ... }
The response comes in the format JWT. If the web server of the communication platform detects a failure of hash verification (in the log log: "Invalid JWT signature"), the 'verify_hash' property can be set in the provider entity and JWT hash verification can be disabled:
{ ... "verify_hash": false, ... }
The web server of the communication platform unpacks the JWT, parses the target values according to the 'query_*' rules. If successful, changes the state ('authorized') and composition of the authorization request, populating it with the parsed contents of the received response.
This completes the session of interaction with the authorization server. Next, the user account is bound to the user account and a session is created in the communication platform.
5. Redirects to the binding page
The user’s web page is redirected to /oauth/enter/<RequestId>.
The 'login_mode', 'register_user_enabled', and 'update_user_enabled' properties define how the authorization server response is bound to the system user account.
The login_mode=auto
value uses the specified 'default_domain' (or explicitly the domain issued by the authorization server and parsed with 'query_domain'), searches by matching the authorization server-issued 'login' value in that domain (parsed with 'query_login'), and creates a web server session.
-
If a user with this login is not found in this domain, the account can be created there if the permission is set simultaneously in the provider entity property 'register_user_enabled' and in the domain parameter 'self_register_allowed'. The user account template specified in the domain parameter is used during creation 'self_register_template'.
-
If a user account is detected, if permission is set in the provider entity property 'update_user_enabled', it is updated based on the actual user information provided by the authorization server and parsed by the rules 'query_*'.
The login_mode=script
value uses the token authorization script (taken from the iam_svcscript_code property or, if empty, from the master domain parameter 'iam_token_svcscript_code'). The authorization request identifier is passed as the first parameter, the second parameter is the JSON representation of the authorization request entity, the third parameter is the JSON representation of the authorization provider entity, and the fourth parameter is the client IP address and port. For successful binding and authorization, the script assigns the value 1 to the variable "result", and assigns the variables "login" and "domain" the corresponding values, by which the system can further find the user account. If necessary, such an account must be created during the script execution. Login and domain can also be generated arbitrarily based on the user information provided by the authorization server and stored in the authorization request (the second scenario parameter).
Some additions
In case the process is unexpectedly interrupted somewhere, the authorization request is automatically removed from the /rest/v1/model/oauth/Requests collection after a few seconds.
In the above scenario there is no basic check if the user is not already authorized, maybe the cookie of the authorization server is already present in the request, and it is necessary to perform only read and bind, without redundant redirects. As a rule, in this case redirections go through without displaying the login and password input window, and it remains invisible to the user. However, this point can be finalized in the communication platform.