BootstrapContext is null on ClaimsIdentity - asp.net-mvc

I have created a new ASP.NET MVC application with .NET 4.5. I have successfully set up authentication with an STS. The authentication flow is working fine and I am able to get the ClaimsIdentity, containing the desired claims, on Thread.CurrentPrincipal.
Now I need the bootstrap token to secure the calls to my service layer. I have set the saveBootstrapContext to true on the identityConfiguration element.
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
However, the BootstrapContext property on the ClaimsIdentity is always null.
var identity = Thread.CurrentPrincipal.Identity as ClaimsIdentity;
var context = identity.BootstrapContext; // context is always null
Am I missing anything here? This was supposed to be straightforward :(

Solved it by these:
<system.identityModel>
<identityConfiguration saveBootstrapContext="true" />
</system.identityModel>
Also need to set TokenValidationParameters.SaveSigninToken, which is distinct from JwtBearerOptions.SaveTokens:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions {
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
SaveSigninToken = true,
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
}
}
);

I ran into this problem when hosting in IIS Express. It turns out that the issue was my browser - I had not closed all of my browser windows or cleared cookies, so the SessionSecurityToken was not being recreated with the new setting, even though the server had been restarted (the existing FedAuth cookie was still being sent from the browser).
Once I forced a re-authentication by closing all browser windows, restarting the browser and performing my request again, the BootstrapContext was present.

If you're using a message handler to manually validate the token using the JwtSecurityTokenHandler to extract a claims principal and attach that to the current Thread, as described here in Using the JWT handler for Implementing “Poor Man”’s Delegation/ActAs, when you're validating the token using JwtSecurityTokenHandler.ValidateToken(), one of the settings on TokenValidationParameters is
SaveBootstrapContext, setting that true does the trick.

I'm using Microsoft.AspNetCore.Authentication.OpenIdConnect, Version=5.0.4.0, and the setting is this:
.AddOpenIdConnect(o =>
{
// . . .
o.TokenValidationParameters.SaveSigninToken = true;
})

Related

Setting SameSite cookie attribute for auth cookie generated by asp.net identity (below NET 4.7.2)

I got bitten by the SameSite cookie attribute enforcement in Google Chrome few days ago. My problem is that I am on .NET 4.7.1 and upgrading the .NET Framework is "ticket based" so the lead time is prohibitive. The site itself uses ASPNET Identity to issue an authentication cookie. Configuration looks like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/"),
CookieName = "cookie-name",
CookieSameSite = SameSiteMode.None,
CookieManager = new SystemWebCookieManagerInNet471(),
CookieSecure = CookieSecureOption.SameAsRequest,
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request)) // Fail handler for HTTP request
{
// Stuff here
}
else
{
ctx.Response.Redirect(ctx.RedirectUri); // Fail handler for AJAX request
}
}
}
});
The workaround to set the SameSite attribute for my case is not ideal at all (URL Rewrite etc).
As per this, this and this I have set the CookieSameSite attribute (see above). However this does not work due to the static constructor acrobatics in here which binds the implementation to later versions of the .NET Framework.
In order to get around this Framework limitation I extended (sort of hacked) the SystemWebCookieManager. Gist (line no 107). Has anyone gone down this road? and hit strange behaviour in production.
I am aware this solution will not work for CSRF or SessionId cookie, but for my case this is ok. I also noticed that there is a Chunked cookie manager too, I think (+ hope) I'll not hit a chunking use case.

Identity Server 4 with EF identity DB - OpenID Connect Failing

PLEASE NOTE: This issue was not resolved in this post. I was asked to create a new post. Please refer to the new post titled:
Identity Server 4 with EF identity DB - OpenID Connect Failing (1)
I have an Identity Server 4 solution with EF Identity DB. I can login with my email and external gmail account, but when I try to login using OpenID (User name and Password) I receive the error below. The issue maybe with the info stored in the Identity DB tables. I'm new to Identity Server and this is my first attempt working with EF Identity DB. I can post DB info if it helps resolve the issue.
Source code:
https://github.com/gotnetdude/GotNetDude-PublicRepository/tree/master/AuthServer
Identity Server Log File:
https://github.com/gotnetdude/GotNetDude-PublicRepository/blob/master/AuthServer_log.txt
MVC Client Log:
https://github.com/gotnetdude/GotNetDude-PublicRepository/blob/master/MVCClient_log.txt
Any suggestions would be appreciated. Paul
EDIT
Please find below the Client configuration info from the identity tables. I'm not sure where to set the AllowedRedirectUris in the DB. The other question I have is why does it work when I sign-in with my email account?
Here is the AuthServer Startup code where I add oidc mvc client as the challenge option ("OpenID Connect") which is fail. The MVC client works fine if I login with the email credentials. I guess is that this has some to do with the way the scope is being handled on the mvc client. Any suggestion are appreciated.
services.AddAuthentication()
.AddGoogle("Google", options =>
{
options.ClientId = "434483408261-55tc8n0cs4ff1fe21ea8df2o443v2iuc.apps.googleusercontent.com";
options.ClientSecret = "3gcoTrEDPPJ0ukn_aYYT6PWo";
})
.AddOpenIdConnect("oidc", "OpenID Connect", options =>
{
//options.Authority = "https://demo.identityserver.io/";
//options.ClientId = "implicit";
//options.SaveTokens = true;
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.ClientId = "mvc";
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
I'm trying to connect to authority (:5000) from the MVC Client (:5002) see image below:
Debug results from AccountService:
There are 3 instances IdentityServer4.EntityFramework.Entities.Client in the client store under context, clients. All 3 instances had there EnableLocalLogin set to True. I hit the break point after selecting OpenID Connect option.
I also set a breakpoint at the top of the login controller, which it never reached:
I think the problem is in line 74 from AuthServer/Startup:
.AddOpenIdConnect("oidc", "OpenID Connect", options =>
{
...
options.ClientId = "mvc";
...
});
The server is not the mvc client. I think this is 'confusing' IdentityServer. You don't need to add oidc to the server. If you remove theses lines then it should work.
If you login from the mvc client website (:5002) then you should be redirected. And if you login to IdentityServer (:5000) you don't have to be redirected. The server is the authority, the resource is identified by a scope and the client by a clientid.
Check the Log files that you have provided. The error that you are receiving is
Invalid redirect_uri: http://localhost:5000/signin-oidc.
If you check your client configuration your AllowedRedirectUris contains http://localhost:5002/signin-oidc.
You have a (typo) mistake in the port. It must be 5002.
EDIT
According to your screenshots, and to the log files, your client is properly configured on the Identity Server side. The problem is in your MVC client, not in the database. You need to look there, and find what RedirectUrl you are setting when starting the client itself.
EDIT 2:
OK, after looking at your code I realized that what #Ruard van Elburg is telling you is the reason for having this problem. When using the Internal authentication, you don't need to specify it like this (you are really confusing Identity Server). This specification is for external Oidc provider only (for example Okta, or whatever else Oidc provider you have). Check here. You see - the Identity Server Startup.cs doesn't contain this code that you have (line 74 to 89 here). Why don't we do this step by step. Try with removing the lines that I mentioned.

Get bootStrapContext token from ClaimsPrincipal in MVC application

I am developing application in MVC and i want to authenticate my users with Azure active directory.
To achieve this, I have created application in Azure active directory with the application URL and also assigned permissions to application.
I have also configured my application to use Azure AD authentication. Now when i try to access the bootstrapContext.Token, it comes always null.
I am using below method to access the bootstrapContext.
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
string userAccessToken = bootstrapContext.Token;
I have searched on this and found that i have to update my web.config. So i have updated it as below
<system.identityModel>
<identityConfiguration saveBootstrapContext="true">
<system.identityModel>
My main goal is to use UserAssertion for authContext.AcquireToken method.
And UserAssertion requires bootstrapContext.Token.
As per suggestion from MvdD, I have also tried below thing
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = metadataUri,
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true
}
});
but now, i am getting below error
AADSTS50027: Invalid JWT token. AADSTS50027: Invalid JWT token. Token format not valid.
Trace ID: 0d052707-9aaf-4037-b7c9-4c4aa7cfcc72
Correlation ID: 9a00573b-cfe9-4665-ab81-c0a03eace9d8
Timestamp: 2016-02-08 05:18:01Z
So can anyone help me on this ?
It's not really clear from your question which protocol or what libraries you are using.
If you are using Katana middleware, you should set the SaveSigninToken property in the appropriate AuthenticationOptions class. For the WS-Federation protocol, it would look something like this:
app.UseWsFederationAuthentication(
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = metadataUri,
TokenValidationParameters = new TokenValidationParameters
{
SaveSigninToken = true
}
}
);
If you are using System.IdentityModel, you need to set the SaveBootstrapContext property on the IdentityConfiguration object.

External Cookie for External Login in ASP.NET OWIN

We have a legacy system which is built on ASP.NET Mvc 4, now we would like to support Signal Sign On via Azure Active Directory for current users as well as new users. Since we have managed our own authentication workflow, ASP.NET Identity definitely does not fit in our case.
I have managed to build a demo which is working on OWIN OpenIdConnect middleware passive mode without using ASP.NET Identity. The below code works correctly:
app.SetDefaultSignInAsAuthenticationType("ExternalCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ExternalCookie",
AuthenticationMode = AuthenticationMode.Passive,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
ClientId = ClientId,
Authority = Authority
// More code
});
And in ExternalLoginCallback action:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var authManager = Request.GetOwinContext().Authentication;
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
//More code to convert to local identity
}
This case is really common even using other providers like Google, Facebook or Twitter. One thing I have not much clear is ExternalCookie, maybe I have missed the whole thing. My understanding is when external login is successfully, external cookie is used to store the external claim identity. And then we call:
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
In order to get the external claim identity and then convert external identity to local identity. I have a little bit confusion why we have to call SignOut external cookie in this case.
Also, I'm not sure whether External Cookie is a must when using external login, or do we have other ways around without using External Cookie.
Please someone give an explanation on this point.
To answer your last question, you change the name of cookie in startup.auth file where you configure external cookie -
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
You can use a string instead of DefaultAuthenticationTypes enum and directly specify the name of the cookie like -
app.UseExternalSignInCookie("myExternalCookie");

MVC5 OWIN ws-federation AuthenticationManager.GetExternalLoginInfoAsync() returns null

I'm trying to setup integrated OWIN WS-Federation (ADFS) authentication in a new MVC 5 project in Visual Studio 2013. WsFederation in Startup.Auth is configured as follows:
app.UseWsFederationAuthentication(wtrealm: "MyRealm",
metadataAddress: "https://myADFSInstanceHost/FederationMetadata/2007-06/FederationMetadata.xml");
Federation button at login page works fine. ADFS login page is achievable, i can log in there.
Required cookies seems to being set properly. At least there is passed .AspNet.ExternalCookie cookie.
But when callback to mvc app is performed, in ExternalLoginCallback controller AuthenticationManager.GetExternalLoginInfoAsync() returns always null.
I know this is an extremely old post, but I've been working on this issue for a week and this is the ONLY resource I've found that provided any sort of help.
The comments on the original post provided exactly what I needed. In order for GetExternalLoginInfo to work, a claim of type NameIdentifier must be present. I was able to mock one of these in Startup.Auth.cs using the following code:
app.UserWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm, //defined earlier
MetadataAddress = adfsMetadata, //also defined earlier
Notifications = new WsFederationAuthenticationNotifications()
{
SecurityTokenValidated = notification =>
{
ClaimsIdentity identity = notification.AuthenticationTicket.Identity;
//loop through all the claims returned (this should return everything set up in ADFS)
foreach (var claim in notification.AuthenticationTicket.Identity.Claims)
{
if (claim.Type == ClaimTypes.Upn) //or whatever claim type you want to use as your name identifier
{
//This line will add a duplicate claim, giving it the specified type. This NEEDS TO BE `NameIdentifier`
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, claim.Value));
}
}
return Task.FromResult(0);
}
}
});

Resources