Sascha Preibisch

Persistent Consent

Blog Post created by Sascha Preibisch Employee on Dec 18, 2017

Hi everybody!

 

This blog post is dedicated to Paul who represents one of our customers. He has asked me to give him an idea on how he could implement persistent consent in OTK until OTK supports that feature out of the box.

 

Persistent Consent vs. Session Consent

In OpenID Connect clients can use parameters such as prompt=none to advise the server to not request consent from the resource_owner again if he has already granted access to his resources in the past. Today OTK remembers a given consent as long as an active refresh_token is available (I call this Session Consent, not sure if there is an official term for that). The token must have been granted for the combination of resource_owner, client_id and scope.

In comparison, Paul and others have said: well, Sascha, the server should remember the decision independently of an active refresh_token (Persistent Consent).

 

Interim solution

We have listened and will provide this feature in a future release of OTK. In order to make this available today I am providing an interim solution which you can implement yourself today.

DISCLAIMER: the official implementation in future OTK may look different so please be prepared to have your users see the consent screen again. Also, my version did not go through QA so please do some testing for yourself before deploying this in production. The status of this provided implementation is this: works on my machine!

 

Implementation for OTK-4.1

Only two policies need to be updated:

  1. /auth/oauth/v2/authorize/login: find a given consent
  2. /auth/oauth/v2/authorize/consent: persist consent

 

In OTK a client can have multiple client_ids. For example, your client SuperCoolApp may have a client_id for the mobile app and one the the JavaScript version of it. In that case you may want to remember the given consent decision not only for the current client_id but for any of that client. This means giving consent for the mobile app will also prevent the resource_owner from seeing the consent screen when using the JavaScript version.

 

Changes in /auth/oauth/v2/authorize/login

Extract the value client_ident from the active session object by adding the two lines below. That value represents the client, not only one single client_id (the line numbers may not match but the 'area' should be correct).

 

Extract client_ident

  • line 121:
    • XPath: /authorize/client_ident
    • Variable Prefix: authorizeClientIdent

 

Next, make up your mind if you want to respect consent decisions of the past only in conjunction with prompt=none or always. By default prompt is required! If you want to ignore the given prompt value disable the branch that checks for it:

Ignore prompt

I am not encouraging you to ignore prompt but I know that I have received this request!

 

Now move existing policies into an All assertions ... container:

Before:

Before using all container

After (moving lines 167, 168, 169 from above into the new All assertions ... on line 167 below):

After using all container

When adding container assertions such as All assertions must ... or At least one assertion ... ALWAYS add comments on the right side that start with // .... This helps reading the policy.

 

The behaviour has not changed yet. Now we are adding logic to look up a consent decision without caring if an active refresh_token exists. This includes the introduction of an At least one assertion ... block. That is required if you want to continue to support Session Consent!:

Logic to support persistent consent

 

Let's go through this line by line:

  • line 169: new At least one assertion ... block
    • this block contains the All assertions ... blocks of line 170 and line 177
    • line 170 handles persistent consent
    • line 177 is the one from above where we moved existing assertions into a new block
  • line 171: find existing consent decision
    • we are using the assertion OTK Session GET which is part of OTK
    • Details:
      • Max age for cache values: 3600
      • Name of cache to be used: otkPersistentConsentCache
      • Key for cache entry: ${resource_owner}${client_ident}
  • line 172 - 173: check if consent exists, extracting the JSON consent message
    • creating the initial consent message is handled further down in this post
    • Details line 173:
      • Encode/ Decode: URL Decode
      • Source Variable: sessionValue.result
      • Target Variable: consentMessage
      • Data Type: Message
      • Content Type: application/json
  • line 174: find granted scope
    • its important to present the Consent screen if the client has not requested the SCOPE in the past (no matter what)! 
    • Details:
      • Expression: granted_scope
      • Other Message Variable: consentMessage
      • Variable Prefix: xpathScope
        • we are not using xpath, but using this name allows us to reuse a few lines of policy further down
  • line 175: find the client
    • to be fail safe extract the client_ident of the past
    • Details:
      • Expression: granted_client
      • Other Message Variable: consentMessage
      • Variable Prefix: granted_client
  • line 176: compare the current client against the one from the past
    • ${client_ident} equlasTo(${granted_client.result})

 

It should look like this:

Result for changes at login

 

Changes in /auth/oauth/v2/authorize/consent

The good news: its just a 2 liner! Look for the block on line 96 of the screenshot below:

Finding the right spot

 

Open that block and go right to the end. It ends with an assertion named OTK Session - Delete. Right after that we will add 2 assertions:

New assertion in consent

 

Lines 155 and 156 are the new ones.

  • line 155: Set Context Variable. This is where we are creating the content for a given consent. You can certainly add other values as you desire
    Consent message
  • line 156: Creating the persistent session
    Consent session

 

That is all there is. Here are some thoughts though:

  • Get an idea on how many consent decisions you want to persist. Specify the value Max. number of entries in the dialog above appropriately
  • Max. database age vs. Max. age for cache: the database age is the persistent memory (this example: 90 days). During this time a consent screen will not be displayed.
    The Cache age is simply the one that helps avoiding accessing the database.
    IMPORTANT: The cache age here has to match the cache age used at /auth/oauth/v2/authorize/login when using OTK Session GET
  • Although this solution works out of the box you could certainly choose to build you own assertion that works with your own, dedicated database table that you may have or want to create for this purpose
  • Client vs. Client_id: if your client has multiple client_ids which are registered for different valid SCOPE values you may not want to use client_ident but the current client_id as part of your session key
  • Revoking Persistent Consent was not covered here. Please think about the scenario on how you want to support resource_owners revoking their given consent. If you are leveraging the revocation endpoint you can add logic there to support it

 

Summary

I hope this post gives you an idea on how you can implement Persistent Consent rather than Session Consent. Even if you are not going for the solution described here it may still give you an idea on how features as these can be implemented very easy. If you are looking forward to see this feature in OTK in the future please give us feedback so that we can build it the way you want it.

 

Paul, I hope this is what you were looking for!

 

As always, thanks for positive feedback and constructive criticism.

Best regards, Sascha

Outcomes