10

I am trying to figure out the best way to run some code once a user has authenticated themselves using OpenID Connect on a Dotnet Core MVC App. I don't want to hard code a redirect URL after sign-in because they still need to end up where they were trying to get to after authentication. I just need to run code eg. "check if its the first time sign-in and set a flag" or something similar.

I was using a middle-ware but since this gets called for every request its causing some problems.

Does anyone have any ideas on how to achieve this?

TejSoft
  • 2,893
  • 5
  • 26
  • 52
Mitch Dart
  • 1,142
  • 1
  • 11
  • 32
  • How did you go with this? I have exactly the same requirement but haven't been able to come up with a suitable solution yet. I posted a similar [question](https://stackoverflow.com/questions/45618386/how-to-redirect-after-azure-ad-authentication-to-different-controller-action-in) – OjM Aug 15 '17 at 13:35
  • How did you solve the problem? I have the exact same issue. – TejSoft Aug 22 '17 at 00:57
  • You should have a method which generates your api token... just call a method in there once the authentication process is complete and have a flag in the users table like AccountNeverLoggedIn... – Jessie Lulham Aug 28 '17 at 23:40
  • @TejSoft Currently what I am doing is using a middleware to check if a account_created claim is set in the claims principle of the logged in user. If not then I know its a first time login then create the claim for the user. Since the check has no IO, just checks the token, there is no cost really for doing it this way. – Mitch Dart Aug 29 '17 at 10:23

3 Answers3

5

I am using OpenIdConnect events to solve this problem.

.AddOpenIdConnect("oidc", options =>
{
   options.Events = new OpenIdConnectEvents
   {
       OnTokenValidated = async ctx =>
       {
           var userID = ctx.Principal.FindFirstValue("sub");

           var db = ctx.HttpContext.RequestServices.GetRequiredService<MyDb>();

           //Do things I need to do with the user here.

       }
   }
}
Mitch Dart
  • 1,142
  • 1
  • 11
  • 32
  • 3
    This runs *every* time the user submits a token, not every time they actually sign in and get said token. If that's a problem for you, see [here](https://stackoverflow.com/a/49691132/2630078). – Kirk Larkin Apr 26 '18 at 13:05
  • 1
    That is definitely a better solution for my original requirement thanks. However now I am also adding claims to the principle on the token so I need it every time a new token is obtained through the back channel. – Mitch Dart Apr 26 '18 at 13:14
0

I believe this type of selective re-routing should be performed within an class inheriting from AuthorizeAttribute; Many preamble actions can be taken in there, including applying additional Role contexts.

One example is reading and accepting Terms and Conditions, which must be performed once for a login credential (read User). This can be performed by a redirect to lower-role protected MVC Controller. Once accepted the Role "TandC_Consent" (or whatever other role) can be applied to the user, and the user can be re-directed to a controller which validates using [AuthorizePermission("TandC_Consent")] every time afterwards.

This github example shows how to implement the AuthorizeAttribute instance and the [AuthorizePermission()] decorator.

JasonInVegas
  • 370
  • 2
  • 9
-1

I was also searching how to do it and I finally I used IdentityServer3. There is IUserService interface which defines some methods you can overload. One of these method is PostAuthenticateAsync From IdentityServer3 documentation:

This method is called after the user has successfully authenticated but before they are returned to the client application. It allows a consolidated place to check for custom user workflows after all of the other authentication methods. It’s designed for UI workflows. Passed a PostAuthenticationContext with these properties: SignInMessage: The contextual information passed to the authorize endpoint. AuthenticateResult: The current AuthenticateResult. The user service can re-assign to a non-null value to change the authentication outcome.

The context parameter has AuthenticateResult property which can be set to a custom authentication result:

public override Task PostAuthenticateAsync(PostAuthenticationContext context)
{
    // code that will determine target url
    // redirectPath = ...
    context.AuthenticateResult = AuthenticateResult(redirectPath, claims);
}

redirectPath must start with / or ~/"

Here is an example of IUSerService implementation.

spatialguy
  • 394
  • 3
  • 11