Configuring the ASP.NET OWIN middleware (MW from now on) in your web app makes it super easy to sent your users to authenticate with Azure AD, as the MW takes care of manufacturing the right message for the Authorization endpoint from the options you provided at startup.

However, there are many situations in which you want to send your user to Azure AD, but instead of the regular sign in settings you specified in the MW option, you want something slightly different to happen.
For example, say that you changed the delegated permissions that your app is requesting for accessing other APIs. The user consent already recorded doesn’t reflect the new permissions you app needs, so (assuming you are using Azure AD v1) you need to ensure that the user has an opportunity to be prompted again for consent and approve the new permissions.
The way you let Azure AD you want to ignore any previously recorded consent and prompt the user anew is to sent prompt=consent in your request to the authorization endpoint. You can add a new action in your controller to trigger the new consent flow, but if you simply call the usual
you’ll end up with whatever message results from the options you initialized you MW with; and you certainly don’t want every sign in request to re-prompt every user for consent, all the time. What now?

The solution is very simple:

  • Right before calling Challenge, Place something in the OWIN context that signals your intention to ask for a brand new consent prompt
  • Implement the RedirectToIdentityProvider notification. Use it to look for whatever you might have placed in the context in step 1 – and if you find it, modify the outgoing message to include prompt=consent

Got it? now let’s actually make it happen. Say that you have an ASP.NET app already hooked up via OIDC MW to Azure AD. If you app is based on the old Katana, you could write your renew consent action as in the following

   1: public void RenewConsent()

   2: {

   3:     HttpContext.GetOwinContext().Set("RenewConsent", "RenewConsent");

   4:     HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },

   5:                     OpenIdConnectAuthenticationDefaults.AuthenticationType);

   6: }

I am using strings because they are nullable, and I am lazy.

Your startup.auth.cs might init the MW as in the following:

   1: app.UseOpenIdConnectAuthentication(

   2:     new OpenIdConnectAuthenticationOptions

   3:     {

   4:         ClientId = clientId,

   5:         Authority = authority,

   6:         PostLogoutRedirectUri = postLogoutRedirectUri,

   7:         Notifications = new OpenIdConnectAuthenticationNotifications

   8:         {

   9:             RedirectToIdentityProvider = OnRedirectToIdentityProvider,


  11:         },

  12:     });

and your implementation of RedirectToIdentityProvider might look like the following:

   1: private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)

   2: {

   3:     var consent = notification.OwinContext.Get<string>("RenewConsent");

   4:     if (consent=="RenewConsent")

   5:     {

   6:         notification.ProtocolMessage.Prompt = "consent";                

   7:     }

   8:     return Task.FromResult(0);

   9: }

Go ahead, try to run your app and trigger the redirect to Azure AD via RenewConsent() – with the Set line active or commented out; you’ll see that Azure AD obediently prompts the user for consent, or not, following your directives.

Using Core in your app? Things are even simpler. Place your signal in the HttpContext (as in HttpContext.Items[“RenewConsent”] = “RenewConsent”) and fetch it in RedirectToIdentityProvider event from the RedirectContext, as in context.HttpContext.Items[“RenewConsent”].


Easy, right? I’ve been wanting to write this post for a long time, as this technique comes in handy in many occasions – the consent renewal is just one example, but there are plenty of other things you might want to tell Azure AD that are situation dependent and can’t be baked in the initialization options (for example, think of this guy). However I never found the time/forgot until it came in handy again for some colleagues last week, so I finally got to it. Yay! Try it out, and hit me here or on twitter if there’s anything unclear Smile enjoy!


Leave a Reply

Your email address will not be published. Required fields are marked *