Important Concepts to Note

These are some important concepts that any implementations should be keenly aware of.

Overriding Parameters

It is possible to fully define a new strategy from scratch. Many times, however, you will simply want to override the settings that are provided for common OIDC providers. In that case, you use the same strategy name, and only provide keys/values for properties that you want to override. If you don't specify a property, it will just use the default configuration property that was provided in the mock_strategies file. If you do specify a key/value pair, that will override that key/value pair that was provided. This is a quick way to get started using an IDP such as google.

JWT IDP Special Handling

Please do NOT create an Authentication Strategy with the name of "jwt". This is a reserved name and trying to use this name will create problems that will be difficult to debug.

Note: While you cannot create a Strategy named "jwt", you can and will OFTEN create a Step named "jwt" within your strategies.

JWT Decoding and Resolution

The ultimate goal of authenticating users is to en up with a JSON object that represents the user's data. In some cases, these JWT are signed and encoded, and in other cases, they are not, or may even be represented as plain JSON objects. To make implementation as simple as possible, there is special behavior regarding any step that is either named "jwt", or if the step name matches the name set on the optional configuration parameter: jwt_key.

In the Google OIDC example that was listed previously in this document, you will notice this step:

{
                "name": "jwt",
                "uri": "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token.id_token}"
}

This endpoint that Google provides will return a plain JSON object of the token that was returned. As such, we don't have to take the additional step of decoding and validating a JWT. Using this service, however, is not recommended for production environments, and most of the time you will be doing the decoding yourself.

If we examine another sample of code, this time for Okta (another IDP), we can see a few steps. The first step, as was indicate earlier, is to redirect the user to the "auth_uri". This is the first implicit step. The follow on steps are:

"steps": [
            {
                "name": "token",
                "uri": "${okta_uri}/token?code=${auth.code}&client_id=${client_id}&client_secret=${client_secret}&redirect_uri=${base_uri}${redirect_path}&grant_type=${grant_type}&state=${state}",
                "method": "POST"
            },
            {
                "name": "jwt",
                "decode": "${token.id_token}"
            }
        ]

As in this case, if the decode parameter is set for a step, then the field that is referenced needs to point to a Base64 encoded JWT object. In turn, the result of that decoding will be set on the user's session, such as this:

req.session[jwt_key] = decode(jwt);

refresh_token

it is often the case that an IDP will provide you with something called a refresh token. This token can kind of be thought to be a case number for this user's authentication, and it can be used to basically refresh the user with a new token without them having to do the extra work of verifying that they want the IDP to share specific details with the SP.

If a step is named "refresh", and if upon validating a user's session it is determined that the access token has expired, then this step will be invoked with the refresh token that was assigned to the user

For instance, consider this configuration code block;

{
                "name": "refresh",
                "uri": "https://oauth2.googleapis.com/token?refresh_token=${refresh_token}&grant_type=refresh_token&client_id=${client_id}&client_secret=${client_secret}",
                "method": "POST",
                "config": {
                    "headers": {
                        "Content-Type": "application/x-www-form-urlencoded"
                    }
                }
            }

This step will NOT get called during the initial user authentication, but it will get called if there is a refresh token that came back as a field in the JWT that was returned from the IDP. For it to work properly, the IDP's initial token must have included a refresh token under the key "refresh_token".