Briefly about OpenID Connect

Original author: Nat Sakimura
  • Transfer

Slowly but inevitably, SAML-based SSO solutions are changing to OpenID stack solutions. Recently, Google has implemented support for the OpenID Connect protocol on its servers. How much it can be acceptable for your project and how to work with it is quite difficult to evaluate according to the protocol specification. This decision should be slightly facilitated by an article by one of the authors of the specification on his blog, which I provide the audience with the translation of. In order to simplify understanding, some points were added on my own, so that I did not have to read the links to the technologies used, but I would recommend to familiarize myself with some of them.


When you read the OpenID Connect specification , you can experience rather unpleasant feelings, from mild fear to complete frustration. All this happens because they are written in a “dry” specification language, and for the most part they describe boundary cases, exceptions, etc. However, when you translate them into normal human language and switch to specific cases, everything becomes pretty obvious. So let's get started! (Remarochka: most of the text matches the original sentence written by David Recordon. Basically, my edits affected only some of the parameter names and other little things)


Creating an OpenID Connect Request


In order for the client to make an OpenID Connect request, it must have the following server information:


  • Client identifier - a unique identifier issued to the client to identify itself on the authorization server.
  • Client key ( client secret ) - a shared secret key established between the authentication server and the client and used to sign requests.
  • The address (endpoint, but in this context, the endpoint and address are synonymous. Next is the address) user authorization ( end-user authorization endpoint ) - URL address of the HTTP request to the server resource capable of authenticating and authorizing the end user.
  • Token issuing address ( token endpoint ) - a resource on the authorization server that provides token issuance.
  • User info endpoint - A protected resource that, upon presenting a token, returns information about the current user to an authorized client.
  • Address verification identifier ( check id endpoint of ) - a protected resource, which, upon presentation of the client verifies the signature identifier and returns information about a user session. ( Removed 2012/3/3: he can return as a common point of introspection OAuth token )
    This information can be obtained as the developer client reading the server and the documentation previously registered applications, and by performing the detection (Discovery) and dynamic registration (Dynamic Registration )

The client builds an OAuth 2.0 request to receive a token.


In order to turn an OAuth 2.0 request into an OpenID Connect request, simply add the OpenID key as one of the required data sets ( scope parameter ). By setting the OpenID key in the parameter, the client requests an identifier for the user, as well as an authentication context. If you want to get the user profile URL, name or photo, you can request additional data sets (for example, a profile). The server (and user) can select profile information available to the client. If the client wants to get the user's email address, he must add the “email” key in the request. The same applies to the address (address) and telephone (phone).
For instance:


GET /authorize?grant_type=token%20id_token&scope=openid%20proflie&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

Although pre-registering your client with the server may not be necessary, it is likely that the servers will have various restrictions and requirements for clients for requests for user information.


Getting OpenID Connect Response


If the user is authorized by the client request, the client will receive a token. An OAuth 2.0 authorization response typically includes two parameters: access_token and id_token . The information in id_token is encoded and includes a JSON object with the following fields:


  • aud (audience) - required field. Client identifier (client_id) for which this id_token is intended.
  • exp (end) - required field. The time after which this token cannot be accepted.
  • sub - required field. Locally unique and never reassigned identifier for the user (subject). For example, "24400320" or "AitOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4".
  • iss (issuer) - required field. HTTPS address: URI indicating the fully qualified host name of the issuer, which, together with user_id, creates a globally unique and never reassigned identifier. For example, " https://aol.com ", " https://google.com ", or " https://sakimura.org ".
  • nonce is a required field. The value set by the server sent in the request.

The id_token parameter provides an easy way to ensure that data received by the client through User-Agent streams (or other untrusted channels) has not been changed. The parameter is signed by the server using a client key that was previously transmitted through a trusted channel. This encoding is called the JSON Web Token (about the JWT in a nutshell and draft specification ). For example, here is a line like this:


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

It consists of three parts that are separated by dots.
The first part is the header (Header), this is a JSON object encoded by Base64url and describing the algorithm and type of token:


{
  "alg": "HS256",
  "typ": "JWT"
}

The second part is Payload, it is also a Base64url encoded JSON object:


{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

The server received the third part as follows:


Алгоритм_цировой_подписи (HS256) (
  base64UrlEncode(первая часть) + "." +
  base64UrlEncode(вторая часть),
  клиентский_ключ
)

Please note that base64url encoding, unlike base64, uses two other characters and does not contain indentation.
The authorization server should issue confirmations about user IDs only within its domains. The client, in turn, must ensure that aud matches its client_id, and iss matches the issuer's domain (including sub-domain) in client_id. The authorization server is responsible for managing its own local namespace and provides local uniqueness and non-repeatability (non-reassignment) of each user_id.
When the client stores the user ID, it must save the tuple from user_id and iss in its local storage. The user_id parameter must not exceed 255 ASCII characters in length.
To verify the authenticity of the data, the client can verify the signature. If the client does not verify the signature, it must make an HTTP request to the identifier verification point in order to verify it. - it’s a little incomprehensible why he should do it


Access to user information (optional)


User information is a regular OAuth 2.0 resource that is returned with a token as a document in JSON format. The client makes an HTTPS "GET" request at the address for providing user information and includes a token as a parameter.
The response is a JSON object that contains some (or all) of the following reserved keys (json object):


  • sub - for example, "AitOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4".
  • profile - URL of the end user profile page
  • name is the display name of the user, for example, "Nat Sakimura".
  • given_name - for example, "Nat".
  • family_name - for example, "Sakimura".
  • email - for example, "sakimura@example.com".
  • picture - for example, " http://graph.facebook.com/sakimura/picture ".

The server, if necessary, can add additional data to this response (for example, such as portable contacts ) until they change the reserved OpenID Connect keys. (Note: there are more clearly defined keys, but for brevity, I will omit their description.)


Opening (optional)


When using OpenID Connect, it is likely that the client may have buttons for registering through popular services, or a text field for entering an email address or URL. OpenID Connect does not directly solve the NASCAR problem
(The NASCAR problem is a link to a jumble of website brand icons through which users log in, emphasizing the similarity of the login page with the collage of stickers for sponsored advertising on track cars in NASCAR races).
The purpose of the stages of opening and registration for a client is to obtain the address of the authorization server, the end address of the token issuing point, client ID, client secret, and obtaining the user data API. If the client is pre-registered on the server, then this information will already be known. Otherwise, the client will need to get them using the opening phase.
The user clicks a button on the client to select a server. In this case, the client’s developer will be able to select the preferred servers and thus, already knowing their authorization addresses (and, possibly, other information). The client may or may not be pre-registered.
In another case, the user (or the User-Agent acting on his behalf) enters the URL or email address. To do this, the client will need to perform a discovery and determine if the authorization server URL is valid. Steps:


  1. We analyze the user input to find out if this is an email address or URL. If it is an email address, do nothing. If there is no scheme, suppose the HTTPS protocol.
  2. We will restore the identifier by reconstructing the various parts.
    For example:
    https://joe.example.com -> https://joe.example.com/
    example.com -> https://example.com/
    joe@example.com -> joe@example.com
  3. We extract the domain and make a WebFinger call through TLS / SSL.
    WebFinger is used to receive information about people or other entities on the Internet using standard HTTP methods over a secure channel. WebFinger returns a JSON object describing the requested entity.
    GET /.well-known/webfinger?resource=acct%3Ajoe%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer   HTTP/1.1
    Host: example.com
    HTTP/1.1 200 OK
    Content-Type: application/jrd+json
    {
    "subject": "acct:joe@example.com",
    "links":
        [{
        "rel": "http://openid.net/specs/connect/1.0/issuer",
        "href": "https://server.example.com"
        }]
    }
  4. In order to get a specific URL, the client adds "/.well-known/openid-configuration" to the issuer and receives the issuer configuration file via TLS / SSL as follows:
    GET /.well-known/openid-configuration HTTP/1.1
    Host: server.example.com

The response is a JSON object that includes the endpoint and other information.
For instance:


{
    "authorization_endpoint": "https://server.example.com/connect/authorize",
    "issuer" : "https://server.example.com",
    "token_endpoint": "https://server.example.com/connect/token",
    "token_endpoint_auth_types_supported":["client_secret_basic", "private_key_jwt"],
    "userinfo_endpoint": "https://server.example.com/connect/user",
    "check_id_endpoint": "https://server.example.com/connect/check_id",
    "registration_endpoint": "https://server.example.com/connect/register"
}

Unregistered customers and dynamic registration (optional)


Regardless of the Discovery mechanism used, the client may or may not already be registered on the server. Servers can have different restrictions on what information clients can receive, depending on whether they are pre-registered (which implies acceptance of the terms of service) or the client uses dynamic registration.
If the client does not have a valid client identifier and key, it can make the following HTTPS POST request for the server registration address (see Opening) with the requested parameters listed in JSON format in the body of the POST request: redirect_uris - Array of URL addresses for receiving OpenID responses.
For instance:


POST /connect/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: server.example.com
{
"redirect_uris":
    ["https://client.example.org/callback",
    "https://client.example.org/callback2"]
}

Before responding to requests, the server must check if the callback URL is registered outside of this OpenID stream. If so, the server will send information with an error response. The server will need to develop a policy to handle such cases when the transmitted redirect_uri has been pre-registered by the client’s developer during dynamic registration requests. Such behavior may mean, for example, that new requests for dynamic registrations with these redirect_uri will lead to an error, but requests using already implemented dynamic registrations will continue to work until they become outdated.
To provide dynamic association, the server includes the following JSON response parameters:


  • client_id - client identifier. This value may change with each response to a request to the server.
  • client_secret - client key. It will definitely change with every answer.
  • expires_at - the number of seconds from 1970-01-01T0: 0: 0Z according to UTC, until client_id and client_secret expire, or 0 if they do not have a statute of limitations.
  • registration_client_uri - uri for managing this registration data.
  • registration_access_token - the token that will be used to access registration_client_uri.

The client needs to store their dynamic registration data to work with server tokens. For each dynamic registration, the client will need to store the client identifier, client key, expiration time, user URL, supported streams, as well as user information API. The end time should be stored as the absolute time or mark that registration will last forever.
As you can see, the basic processes of the OpenID Connect web client are quite simple, and as simple as those originally proposed. At the same time, additional functionality can be used, for example, requesting specific data sets, rather than the default set. These additional features are available when they are needed and do not turn simple interactions into major problems for customers with a large number of OpenID providers.


Also popular now: