OpenID Connect ASP NET MVC AAD - asp.net-mvc

I implemented a sample app using OpenID Connect standard with ASP NET MVC website. My goal was to outsource storing sensitive data to Azure so i used Azure Active Directory. Since it's impossible to add custom properties to users in Azure i store non sensitive user Claims in our private db. I managed to get this claims and "add" them to the cookie like this:
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = context =>
{
var objectId = context.AuthenticationTicket.Identity.Claims.First(x => x.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier");
var claims = GetUserClaims(objectId.Value);
foreach (var item in claims)
{
context.AuthenticationTicket.Identity.AddClaim(new System.Security.Claims.Claim(item.Key, item.Value));
}
return Task.FromResult(0);
}
}
This way I added required claims to the cookie so those claims persist in my User object until user sign-out which is fine but there is one Claim which can change during the session ( basically user can change it on one page ). The problem is I can't find how to "change" this Claim in the cookie so it will persist. Ideally I would like to somehow force
AuthorizationCodeReceived
function to be called again. Is it possible ? Or is there another way where I can swap the value stored in the cookie ?
So far my only solution is to log-out user when he change this value so it will force him to sign-out again and my callback for AuthorizationCodeReceived will be called again, but it's not a very user-friendly way.

You can call HttpContext.GetOwinContext().Authentication.SignIn() after you add a claim in identity object to persist the new claim in cookie.

Related

Initiate and store multiple OAuth2 external authentication challenges in a ASP.NET Core MVC application?

I can authenticate against two separate OAuth authentication schemes but it seems only one can be active at a time. I'd like to compare data from two separate SaaS applications and therefore I need two separate Bearer tokens. How can I initiate multiple OAuth challenges when the user loads the application and then store the Bearer Tokens for each? (e.g. in the Context.User cookie?)
My Startup.cs is as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/signin";
options.LogoutPath = "/signout";
})
.AddScheme1 (options =>
{
options.ClientId = Configuration["Scheme1:ClientId"];
options.ClientSecret = Configuration["Scheme1:ClientSecret"];
options.Scope.Add("scope1");
options.SaveTokens = true;
})
.AddScheme2(options =>
{
options.ClientId = Configuration["Scheme2:ClientId"];
options.ClientSecret = Configuration["Scheme2:ClientSecret"];
options.Scope.Add("scope1");
options.SaveTokens = true;
});...
}
The AuthenticationController calls the Challenge overloaded method from the Microsoft.AspNetCore.Mvc.Core assembly that takes a single provider/scheme (passing multiple schemes in the overloaded method seems to be ignored).
[HttpGet("~/signin")]
public async Task<IActionResult> SignIn() => View("SignIn", await HttpContext.GetExternalProvidersAsync());
[HttpPost("~/signin")]
public async Task<IActionResult> SignIn([FromForm] string provider)
{
...
return Challenge(new AuthenticationProperties { RedirectUri = "/" }, provider);
}
Presumably, you'd prompt the user to sign-into one external application, redirect back to the home page, and then prompt them to sign-into the second one, and then allow them to start using the application proper.
If this is possible - e.g. using a "multiple" Auth cookie - how then would I fetch the correct Bearer token and User values for the given scheme? Currently you just seem to fetch the token with a generic "access_token" name and unique user values:
string accessToken = await HttpContext.GetTokenAsync("access_token");
string userID = User.FindFirstValue(ClaimTypes.NameIdentifier);
There does seem to be some information here regarding using a SignInManager but I'm unable to determine if this is applicable to this problem.
I would aim to start with a standard architecture where the user authenticates with the one and only app, and gets only one set of tokens, issued by your own Authorization Server.
SaaS DATA - OPTION 1
Does the user need to get involved in these connections or can you use a back end to back end flow here?
Your C# code could connect to the SaaS provider with the client credentials grant, using the client ID and secret that you reference above. Provider tokens would then be cached in memory, then used by the back end code to return provider data to the UI. This is a simple option to code.
SaaS DATA - OPTION 2
If the user needs to get involved, because the data is owned by them, you might offer UI options like this. After each click the user is redirected again, to get a token for that provider.
View provider 1 data
View provider 2 data
Aim to emulate the embedded token pattern, where the provider tokens are available as a secondary credential. How you represent this could vary, eg you might prefer to store provider tokens in an encrypted cookie.
CODING AND SIMPLICITY
I would not mix up provider tokens with the primary OAuth mechanism of signing into the app and getting tokens via the .NET security framework, which typically implements OpenID Connect. Instead I would aim to code the SaaS connections on demand.
I think you will find it easier to code the SaaS connections with a library approach, such as Identity Model. This will also help you to deal with SaaS provider differences more easily.
I assume you use OIDC schemes.
First, you need to add two cookie schemes, one for each OIDC authentication scheme as their sign in scheme and set their callback path to different values to stop them competing:
services.AddAuthentication()
.AddCookie("Cookie1")
.AddCookie("Cookie2")
.AddOpenIdConnect("OidcScheme1", opt =>
{
opt.SignInScheme = "Cookie1";
opt.CallbackPath = "/signin-oidc-scheme1";
opt.SaveTokens = true;
})
.AddOpenIdConnect("OidcScheme2", opt =>
{
opt.SignInScheme = "Cookie2";
opt.CallbackPath = "/signin-oidc-scheme2";
opt.SaveTokens = true;
});
This will instruct the OIDC handler to authenticate the user from corresponding cookie.
Second, you need a controller action to challenge the user against each OIDC scheme:
[HttpGet]
[Route("login")]
[AllowAnonymous]
public IActionResult Login([FromQuery]string scheme,
[FromQuery]string? returnUrl)
{
return Challenge(new AuthenticationProperties
{
RedirectUri = returnUrl ?? "/"
}, scheme);
}
From your web app, you need to send the user to the Login endpoint twice with different scheme values:
GET /login?scheme=OidcScheme1
GET /login?scheme=OidcScheme2
Or chain them together using the returnUrl:
GET /login?scheme=OidcScheme1&returnUrl=%2Flogin%3Fscheme%3DOidcScheme2
Once signed in, there should be two cookies in the browser window, for example:
To authenticate the user and restore both identities from two cookies, you can use authorization policy:
[HttpGet]
[Authorize(AuthenticationSchemes = "OidcScheme1,OidcScheme2")]
public async Task<IActionResult> SomeOperation()
{
// Two identities, one from each cookie
var userIdentities = User.Identities;
...
}
To get access token from each authentication scheme, use the method you discovered (GetTokenAsync) and specify authentication scheme:
var token1 = await HttpContext.GetTokenAsync("OidcScheme1", "access_token");
var token2 = await HttpContext.GetTokenAsync("OidcScheme2", "access_token");
It is possible that the access token is not returned from the token endpoint depends on the response_type you used. If this is the case, try set the OpenIdConnectionOptions.ResponseType to OpenIdConnectResponseType.Code and make sure the scope is correct.
I encountered a similar problem where we had microservices that are/were shared across multiple products with each product having a separate IDP tenant (essentially a different token issuer). Perhaps a similar approach might work for your scenario...
The following link helped me with a solution - see here.
Basically I defined a smart authentication scheme
var builder = services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = "smart";
//...
});
Then in the smart policy scheme definition, I decode the JWT coming in to work out the issuer from the iss claim in the JWT, so that I can forward to the correct location for JWT bearer authentication.
builder.AddPolicyScheme("smart", "smart", options =>
{
options.ForwardDefaultSelector = context =>
{
var jwtEncodedString = context.Request.Headers["Authorization"].FirstOrDefault()?.Substring(7);
if (string.IsNullOrEmpty(jwtEncodedString))
return settings.Tenants.First().Key; // There's no authorization header, so just return any.
var token = new JwtSecurityToken(jwtEncodedString: jwtEncodedString);
var issuer = token.Claims.First(c => c.Type == "iss").Value?.TrimEnd('/');
var tenant = settings.Tenants
.Where(pair => pair.Value.Issuer.TrimEnd('/') == issuer)
.Select(pair => pair.Key).FirstOrDefault();
if (tenant == null)
throw new AuthorizationException($"Failed to locate authorization tenant with issuer '{issuer}'.");
return tenant;
};
});
Note: settings.Tenants is just an array of whitelisted tenants (from appsettings) that I configure as follows:
foreach (var tenant in settings.Tenants)
builder.AddJwtBearer(tenant.Key, options => Configure(options, tenant.Value, defaultJwtBearerEvents));

How does ADFS keep a user authenticated?

I'm using WsFed to implement ADFS SSO into an app. If I try to run [Authorize] methods, I'm taken to the sign in page. When I sign in, a cookie with encrypted information is created and I'm able to run [Authorize] methods. The cookie has option ExpireTimeSpan = TimeSpan.FromSeconds(10);. So far, this works as expected and an unauthorized user cannot access the app.
The confusion begins when the cookie expires, is altered, or deleted from the browser. When this happens, if I run an [Authorized] method I'm automatically signed in again without needing to reenter my credentials and the cookie is recreated. However, if I explicitly sign out using return SignOut(... method, then I am required to reenter my credentials.
Why does ADFS re-authenticate me if I delete the cookie, and how does it know to do so? It doesn't do it if I explicitly sign out. Shouldn't remaining authenticated depend on the cookie being present with the correct values?
Authentication setup in Startup.ConfigureServices:
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
})
.AddWsFederation(options =>
{
options.Wtrealm = Configuration["AppSettings:wsfed:realm"];
options.MetadataAddress = Configuration["AppSettings:wsfed:metadata"];
options.UseTokenLifetime = false;
})
.AddCookie(options =>
{
options.Cookie.Name = "AuthenticationCookie";
options.LoginPath = "/signin-wsfed";
options.LogoutPath = "/NameController/Logout";
options.ExpireTimeSpan = TimeSpan.FromSeconds(10);
options.SlidingExpiration = true;
});
Login action:
[AllowAnonymous]
[HttpGet]
public IActionResult Login()
{
var authProperties = new AuthenticationProperties
{
RedirectUri = "https://app:1234/NameController/Index",
};
return Challenge(authProperties, WsFederationDefaults.AuthenticationScheme);
}
Logout action:
[AllowAnonymous]
[HttpGet]
public IActionResult SignOutOfADFS()
{
return SignOut(
new AuthenticationProperties
{
RedirectUri = "https://app:1234/NameController/AfterLogout"
},
CookieAuthenticationDefaults.AuthenticationScheme,
WsFederationDefaults.AuthenticationScheme);
}
The AD FS is an identity provider that is commonly used for single sign-on purposes. As part of that, a key feature is that the AD FS does remember the signed-in user in order to authenticate them for another website. It does that by remembering the user using a separate session persisted using a cookie for the AD FS website.
When you sign out locally from your application, then all you are doing is clearing your local cookie. So when you try to authenticate again and the user is challenged to authenticate with the identity provider, the AD FS is able to sign the user in without asking them again for their credentials. For the AD FS your application is then just like a third website which is asking for authentication after the user already signed in into the AD FS.
In order to sign out completely, you will have to do a WSFederation sign out. As part of that process, the local cookie is cleared and then the user is redirected to an AD FS signout page where the AD FS authentication cookie is also cleared. On a subsequent authentication attempt, the AD FS then cannot remember the user anymore (since there’s no cookie) so they have to authenticate again with their credentials. That is what you are doing in your SignOutOfADFS action.
The WSFederation protocol supports a way for the authenticating application to require the user to reauthenticate with the identity provider by passing the wfresh=0 parameter with the authentication request. This is also supported in current AD FS versions. Unfortunately, I don’t think this parameter is currently supported by the WSFederation authentication handler for ASP.NET Core. It wouldn’t really prevent the user from reusing their authentication though, so you wouldn’t be able to use this a security feature.

MVC App using Azure AD with ADAL 3 - Authentication Cookie expires after 1 hour

I work on an MVC Web Application using Azure AD with OAuth 2 and Open ID Connect for Authorization of users.
Per documentation tokens are refreshed automatically when a token expires after 60 minutes (which is fine).
Now the problem is, to acquire a token I need to know the currently authenticated user which is stored in a cookie. The code to acquire a Token is like this:
public async Task<AuthenticationToken> GetTokenForApplication(string resourceID)
{
string signedInUserID = ClaimsPrincipal.Current.SignedinUserId();
var tenantID = ClaimsPrincipal.Current.TenantId();
string userObjectID = ClaimsPrincipal.Current.SignedinUserObjectId();
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(Config.ClientId, Config.AppKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
AuthenticationContext authenticationContext = new AuthenticationContext(string.Format("{0}{1}", Config.AadInstance, tenantID), new ADALTokenCache(signedInUserID));
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(resourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
var token = new AuthenticationToken(authenticationResult.AccessToken) { ExpiresOn = authenticationResult.ExpiresOn };
return token;
}
Now I am in the dilemma, that the ClaimsPrincipal.Current.SignedinUserId() method call throws a null reference exception. When I inspect the ClaimsPrincipal.Current object, no data about the logged in user is available. But this is the Information needed to renew / request a token.
What is the best practice in an MVC Web App? Is there a way to extend the validity of the cookie or is there any way to reauthenticate the current user without redirecting to the root page of the web application?
After doing more research I have found these two pages which describe some options to deal with my problem pretty good:
Controlling a Web App’s session duration
and ASP.NET-Identity-Cookie-Authentication-Timeouts
are these good approaches?
After doing more research I have found these two pages which describe some options to deal with my problem pretty good:
Controlling a Web App’s session duration
and ASP.NET-Identity-Cookie-Authentication-Timeouts
are these good approaches?

Refreshing access tokens in IdentityServer4 clients

I wonder how to refresh a access token in a IdentityServer4 client using the hybrid flow and which is built using ASP.NET Core MVC.
If I have understood the whole concept correctly the client first need to have the "offline_access" scope in order to be able to use refresh tokens which is best practice to enable short lived access tokens and ability to revoke refresh tokens preventing any new access tokens to be issued to the client.
I successfully get a access token and a refresh token, but how should I handle the actual update procedure of the access token in the MVC client?
Can the OpenId Connect (OIDC) middleware handle this automatically? Or should I rather check the expire time of the access token everywhere I call WEB Api's by basically check if the access token have expired or will expire very soon (upcoming 30 seconds) then refresh the access token by calling the token endpoint using the refresh token?
Is it recommended to use the IdentityModel2 library TokenClient extension method RequestRefreshTokenAsync in my Controller action methods for calling the token endpoint?
I have seen code that in the OIDC middleware events request access token and using the response store a claim containing a expire datetime. The problem is that my OIDC in somehow already request a access token automatically so it doesn't feel good to request a new access token directly after recieving the first one.
Example of a Controller action method without access token refresh logic:
public async Task<IActionResult> GetInvoices()
{
var token = await HttpContext.Authentication.GetTokenAsync("access_token");
var client = new HttpClient();
client.SetBearerToken(token);
var response = await client.GetStringAsync("http://localhost:5001/api/getInvoices");
ViewBag.Json = JArray.Parse(response).ToString();
return View();
}
The OIDC middleware will not take care of this for you. It's being executed when it detects a HTTP 401 response, it then redirects the user to IdentityServer login page. After the redirection to your MVC application, it will turn claims into a ClaimsIdentity and pass this on to the Cookies middleware which will materialise that into a session cookie.
Every other request will not involve the OIDC middleware as long as the cookie is still valid.
So you have to take care of this yourself. Another thing you want to consider is that whenever you're going to refresh the access token, you'll have to update the existing one so you don't lose it. If you don't do this, the session cookie will always contain the same token - the original one - and you'll refresh it every time.
A solution I found is to hook that into the Cookies middleware.
Here's the general flow:
On every request, use the Cookies middleware events to inspect the access token
If it's close to its expiration time, request a new one
Replace the new access and refresh tokens in the ClaimsIdentity
Instruct the Cookies middleware to renew the session cookie so it contains the new tokens
What I like with this approach is that in your MVC code, you're pretty much guaranteed to always have a valid access token, unless refereshing the token keeps failing several times in a row.
What I don't like is that it's very tied to MVC - more specifically the Cookies middleware - so it's not really portable.
You can have a look at this GitHub repo I put together. It indeed uses IdentityModel as this takes care of everything and hides most of the complexity of the HTTP calls you'd have to make to IdentityServer.
I created a solution based on a action filter togheter with the OIDC middleware in ASP.NET Core 2.0.
AJAX requests will also go via the action filter hence update the access token/refresh token.
https://gist.github.com/devJ0n/43c6888161169e09fec542d2dc12af09
I found two possible solutions, both are equal but happens at different times in the OIDC middleware. In the events I extract the access token expire time value and store it as a claim which later can be used to check if it's OK to call an Web API with the current access token or if I rather should request a new access token using the refresh token.
I would appreciate if someone could give any input on which of these events are preferable to use.
var oidcOptions = new OpenIdConnectOptions
{
AuthenticationScheme = appSettings.OpenIdConnect.AuthenticationScheme,
SignInScheme = appSettings.OpenIdConnect.SignInScheme,
Authority = appSettings.OpenIdConnect.Authority,
RequireHttpsMetadata = _hostingEnvironment.IsDevelopment() ? false : true,
PostLogoutRedirectUri = appSettings.OpenIdConnect.PostLogoutRedirectUri,
ClientId = appSettings.OpenIdConnect.ClientId,
ClientSecret = appSettings.OpenIdConnect.ClientSecret,
ResponseType = appSettings.OpenIdConnect.ResponseType,
UseTokenLifetime = appSettings.OpenIdConnect.UseTokenLifetime,
SaveTokens = appSettings.OpenIdConnect.SaveTokens,
GetClaimsFromUserInfoEndpoint = appSettings.OpenIdConnect.GetClaimsFromUserInfoEndpoint,
Events = new OpenIdConnectEvents
{
OnTicketReceived = TicketReceived,
OnUserInformationReceived = UserInformationReceived
},
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = appSettings.OpenIdConnect.NameClaimType,
RoleClaimType = appSettings.OpenIdConnect.RoleClaimType
}
};
oidcOptions.Scope.Clear();
foreach (var scope in appSettings.OpenIdConnect.Scopes)
{
oidcOptions.Scope.Add(scope);
}
app.UseOpenIdConnectAuthentication(oidcOptions);
And here is some event examples I'm can choose among:
public async Task TicketReceived(TicketReceivedContext trc)
{
await Task.Run(() =>
{
Debug.WriteLine("TicketReceived");
//Alternatives to get the expires_at value
//var expiresAt1 = trc.Ticket.Properties.GetTokens().SingleOrDefault(t => t.Name == "expires_at").Value;
//var expiresAt2 = trc.Ticket.Properties.GetTokenValue("expires_at");
//var expiresAt3 = trc.Ticket.Properties.Items[".Token.expires_at"];
//Outputs:
//expiresAt1 = "2016-12-19T11:58:24.0006542+00:00"
//expiresAt2 = "2016-12-19T11:58:24.0006542+00:00"
//expiresAt3 = "2016-12-19T11:58:24.0006542+00:00"
//Remove OIDC protocol claims ("iss","aud","exp","iat","auth_time","nonce","acr","amr","azp","nbf","c_hash","sid","idp")
ClaimsPrincipal p = TransformClaims(trc.Ticket.Principal);
//var identity = p.Identity as ClaimsIdentity;
// keep track of access token expiration
//identity.AddClaim(new Claim("expires_at1", expiresAt1.ToString()));
//identity.AddClaim(new Claim("expires_at2", expiresAt2.ToString()));
//identity.AddClaim(new Claim("expires_at3", expiresAt3.ToString()));
//Todo: Check if it's OK to replace principal instead of the ticket, currently I can't make it work when replacing the whole ticket.
//trc.Ticket = new AuthenticationTicket(p, trc.Ticket.Properties, trc.Ticket.AuthenticationScheme);
trc.Principal = p;
});
}
I also have the UserInformationReceived event, I'm not sure if I should use this instead of the TicketReceived event.
public async Task UserInformationReceived(UserInformationReceivedContext uirc)
{
await Task.Run(() =>
{
Debug.WriteLine("UserInformationReceived");
////Alternatives to get the expires_at value
//var expiresAt4 = uirc.Ticket.Properties.GetTokens().SingleOrDefault(t => t.Name == "expires_at").Value;
//var expiresAt5 = uirc.Ticket.Properties.GetTokenValue("expires_at");
//var expiresAt6 = uirc.Ticket.Properties.Items[".Token.expires_at"];
//var expiresIn1 = uirc.ProtocolMessage.ExpiresIn;
//Outputs:
//expiresAt4 = "2016-12-19T11:58:24.0006542+00:00"
//expiresAt5 = "2016-12-19T11:58:24.0006542+00:00"
//expiresAt6 = "2016-12-19T11:58:24.0006542+00:00"
//expiresIn = "60" <-- The 60 seconds test interval for the access token lifetime is configured in the IdentityServer client configuration settings
var identity = uirc.Ticket.Principal.Identity as ClaimsIdentity;
//Keep track of access token expiration
//Add a claim with information about when the access token is expired, it's possible that I instead should use expiresAt4, expiresAt5 or expiresAt6
//instead of manually calculating the expire time.
//This claim will later be checked before calling Web API's and if needed a new access token will be requested via the IdentityModel2 library.
//identity.AddClaim(new Claim("expires_at4", expiresAt4.ToString()));
//identity.AddClaim(new Claim("expires_at5", expiresAt5.ToString()));
//identity.AddClaim(new Claim("expires_at6", expiresAt6.ToString()));
//identity.AddClaim(new Claim("expires_in1", expiresIn1.ToString()));
identity.AddClaim(new Claim("expires_in", DateTime.Now.AddSeconds(Convert.ToDouble(uirc.ProtocolMessage.ExpiresIn)).ToLocalTime().ToString()));
//identity.AddClaim(new Claim("expires_in3", DateTime.Now.AddSeconds(Convert.ToDouble(uirc.ProtocolMessage.ExpiresIn)).ToString()));
//The following is not needed when to OIDC middleware CookieAuthenticationOptions.SaveTokens = true
//identity.AddClaim(new Claim("access_token", uirc.ProtocolMessage.AccessToken));
//identity.Claims.Append(new Claim("refresh_token", uirc.ProtocolMessage.RefreshToken));
//identity.AddClaim(new Claim("id_token", uirc.ProtocolMessage.IdToken));
});
}

OWIN WsFederation authentication with in-app authorization

The scenario:
A ASP.NET MVC Web app for a company's internal users, configured for authentication against the company's ADFS, using Microsoft.Owin.Security.WsFederation
The company's ADFS contains several users, but only a few of them should be able to log in to the application.
I therefore have a database table containing these users' email addresses
The web app should check if the email claim received from ADFS exists in the DB table, and only issue a log in token for those users.
Other users should be redirected to a "Sorry you are not authorized to use this application"-page.
My question:
Where is the correct place to put the authorization logic that checks if an user should be allowed in?
Here's the code in my Startup.Configuration method:
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
MetadataAddress = "https://.../FederationMetadata.xml",
Wtrealm = "...",
}
);
You have two options to achieve what you want:
1. One is to configure this at the very AD FS. I personally think this is the right way to do this, since the AD FS is the IdP and it should be the one controlling whether or not its users have access to the application. In this case the company should or should not allow somebody to use some of its resources (of course there are anti-arguments). This can be easily done at the Domain Controller, through the AD FS Management GUI. The following answer greatly describes this:
https://serverfault.com/a/676930/321380
2. Second is to use the Notifications object at the OWIN WSFed middleware in this way:
Notifications = new WsFederationAuthenticationNotifications()
{
SecurityTokenValidated = (context) =>
{
//extract claims' values and check identity data against your own authorization logic
bool isAuthorized = CheckForUnauthorizedAccess();
if (!isAuthorized)
{
throw new SecurityTokenValidationException("Unauthorized access attemp by {some_identifier}");
}
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
if (context.Exception is an unauthorized exception)
{
context.OwinContext.Response.Redirect("<unauthorized_redirect_url>");
}
context.HandleResponse(); // Suppress the exception
//exception logging goes here
return Task.FromResult(0);
}
}

Resources