aID has a basic implementation of OpenID Connect (OIDC), a layer buildt on top of OAuth 2.0 that makes common integrations easier by adding data about the user to the OAuth 2.0 response, so the caller doesn't have to implement support for a custom endpoint for getting that data right after getting the OAuth access_token
.
Everything you've read about our OAuth 2.0 implementation still applies, there are just a couple of extra tricks you need to trigger OIDC-mode:
To trigger OIDC-mode you need to request the openid
scope, but before you can do this, your OAuth 2.0 client (In OIDC called a Relying Party (RP)) needs to have access to this scope. This must be configured server side by aID.
Once the openid
-scope has been configured for the client, the client can act as a Relying Party. This is done by adding openid
as a requested scope during authorization.
https://www.aid.no/api/oauth/authorize/www.glomdalen.no?client_id=c4feb4b3&response_type=code&redirect_uri=https%3A%2F%2Fwww.example.com%2Faid_login&scope=openid+uuid+name+email&nonce=somethingrandom
The important part here is the openid
-part after scope=
. aID now knows that you want to have an id_token
included in the response of the token endpoint after authorization. The flow will be the same as for normal authorization.
Another thing to note is that you should add a nonce
, which is a random string that you add to authorization, and later checks inside the id_token to make sure there is no man in the middle doing a replay attack. This is recommended, but not required.
OpenID Connect defines a standardized mode of triggering what we call stealth mode, which you can read about in 🟢Single signon and signout . In OIDC, this is triggered by adding a prompt
parameter to the authorization request with the value none
. When this is done, the user will not be presented with any aID user interfaces, instead the user is returned to redirect_uri
with an error if the user can not be authorized without presenting a user interface.
The following errors can occur:
The user is not currently logged in, so in a normal flow, the user would be presented with the login page.
The user is logged in, but has not previously agreed to share aID data with the Relying Party. In a normal flow, the user would be presented with a page where the user accepts an agreement with the legal entity (Amedia, Aller etc.) owning the Relying Party.
After the user has completed authorization, the user is sent back to requsted_uri as per normal OAuth 2.0. In the URL you will find a grant code that you pass to the token URL to get the OAuth access token. The only difference in OIDC-mode is that the response for this service now includes id_token, which is a JWT containing claims about the user. These claims can be used instead of calling separate endpoints for getting the same data.
curl -H 'Authorization: Basic YzRmZWI0YjM6MWMzYjAwZDQ=' -F grant_type=authorization_code -F code=d34db33f -F redirect_uri=https://www.example.com/aid_login -X POST https://www.aid.no/oauth/token
Now the response will look a little different than before:
{
"access_token": "d4bbad00",
"token_type": "bearer",
"scope": "id name email",
"created_at":1422988263,
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3d3dy5haWQubm8vIiwiYXVkIjoiYzRmZWI0YjMiLCJzdWIiOiI1OWNiNjM3Mi1kM2RiLTExZTUtYWIzMC02MjU2NjI4NzA3NjEiLCJuYW1lIjoiRG9uYWxkIER1Y2siLCJleHAiOjE2NjE1MjI1ODgsImlhdCI6MTY2MTUyMTk4OCwibm9uY2UiOiJzb21ldGhpbmdyYW5kb20ifQ.BVzmVa3yiY9OxS_cuQh9pcSw0sEv7BQoFEzQPXla5mVxZfCndobICFlcQtMZtpH8m8H5uGmkMDbgDQWnyfZfO4mKXcpbKQh1I4-fx7pQZjKBCK_yGi0DKguwuj6EpEq6xi7Y_ZRyhRTH90_wTSfVB9ECou6kNoxZFjBAb93ucYySW-nw_iVEhp_rQFvCWpDpzQJC9Jlb_0vXNcApKso28d0p047fP97HGi_GLqk3PuRSyR6M3pi8nQ82jPkrOV_V4GGAReyFMQ-NdhFIao6lSn2LEiZuoAsAmp9HdcTEaJ7jzftu4YB6tRBZ36g0ZUuroXb2KXxx7drhJ5Eg1owpig"
}
The new part here is the id_token
. This looks like random garbage, but is in fact an encoded JWT. This can be decoded, and the signature can be verified, into a JSON like this:
{
"iss": "https://www.aid.no/",
"aud": "c4feb4b3",
"sub": "59cb6372-d3db-11e5-ab30-625662870761",
"name": "Donald Duck",
"exp": 1661522588,
"iat": 1661521988,
"nonce": "somethingrandom"
}
Now we immediately know a lot about the user, without having to call any of the endpoints described in 🟢Services . And OpenID Connect defines most of these claims (attributes), so the response should be compatible accross different ID-providers.
In this response we see that the JWT was issued by aID for Relying Party (OAuth Client) with client_id
c4feb4b3. The subject here is the aID user with uuid
59cb6372-d3db-11e5-ab30-625662870761, who is called Donald Duck.
The response also tells us when the JWT expires and it lists the nonce
, which should be verified to be the same as sent in the authorization request.
The token_id also contains a crypto-part describing how the JWT was signed:
{
"typ"=>"JWT",
"alg"=>"RS256",
"kid"=>"5ca1ab1e"
}
Here we can se that this is a JWT signed using the RS256 algorithm using a key with ID 5ca1ab1e.
The signature of the JWT should be verified to match our public key. The key can be downloaded from this URL:
https://www.aid.no/oauth/discovery/keys
The response will be a JSON containing a list of keys. One of them should match the kid of the crypto-part of the JWT.
Example response:
{
"keys": [
{
"kty": "RSA",
"kid": "5ca1ab1e",
"e": "AQAB",
"n": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u+qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyehkd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdgcKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbcmwIDAQAB",
"use": "sig",
"alg":"RS256"
}
]
}
OpenID Connect lists a set of standard claims, some of which are implemented by aID. Others can be implemented upon request, even claims not defined by OIDC. The implementation utilizes our public user API, anything this API can output can easilly be mapped to claims.
Claims are closelly tied to scopes, so you need a certain scope to get certain claims.
Currently implemented claims:
Issuer. Standard OpenID claim.
Audience. Standard OpenID claim. Corresponds to client_id.
Issued At. Standard OpenID claim.
Expires At. Standard OpenID claim
Nonce given in authorization request. Standard OpenID claim.
User's ID. Uniquelly and permanently identifies the current user.
Pseudonym key used for tracking user on websites.
User's mobile phone number
User's profile picture (uploaded or from Facebook, with fallback)
User's privacy preferences (allow_research_usage, connect_with_surveys, personalized_ads and personalized_content as boolean attributes).
List of sites the user has access to (list of attributes with site_domain and access_features)
The configuration can be obtained from this URL:
Example response:
{
"issuer": "https://www.aid.no/",
"authorization_endpoint": "https://www.aid.no/oauth/authorize",
"token_endpoint": "https://www.aid.no/oauth/token",
"revocation_endpoint": "https://www.aid.no/oauth/revoke",
"introspection_endpoint": "https://www.aid.no/oauth/introspect",
"userinfo_endpoint": "https://www.aid.no/oauth/userinfo",
"jwks_uri": "https://www.aid.no/oauth/discovery/keys",
"scopes_supported": ["uuid","name","avatar","openid","profile","id","email","phone","birth_date","tracking_key","access","external_accounts","privacy_preferences","groups","two_factor_enabled"],
"response_types_supported": ["code"],
"response_modes_supported": ["query","fragment","form_post"],
"grant_types_supported": ["authorization_code"],
"token_endpoint_auth_methods_supported": ["client_secret_basic","client_secret_post"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"claim_types_supported": ["normal"],
"claims_supported": ["iss","sub","aud","exp","iat","name","pse"]}
The details will change in the future when more stuff is added etc. But here are the most important parts:
This is where you send the user to start OpenID/OAuth authorization
This is the endpoint to call after the user returns to redirect_uri and you want to get an access token or OpenID token.
This is where you can log the user out
Here you can get OpenID user info without doing a new authorization
Call this if you need status information about an access token
This is where you can get public keys to verify the OpenID JWT signature