Unable to redirect uri after cas authentication with WSO2 provider using asp.net core authorization - authenticationchallenge

[enter image description here][1]I am trying to authenticate my .net core application using cas with Wso2 identity provider although the autentication is succesful but on redirect uri i am getting webpage not found error with cas ticket not able to figure out what is the issue any guidance would be helpful
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Login";
options.Events.OnSigningOut = context =>
{
var redirectContext = new RedirectContext<CookieAuthenticationOptions>(
context.HttpContext,
context.Scheme,
context.Options,
context.Properties,
"/"
);
if (builder.Configuration.GetValue("Authentication:CAS:SingleSignOut", false))
{
// Single Sign-Out
var casUrl = new Uri(builder.Configuration["Authentication:CAS:ServerUrlBase"]);
var links = context.HttpContext.RequestServices.GetRequiredService<LinkGenerator>();
var serviceUrl = context.Properties.RedirectUri ?? links.GetUriByPage(context.HttpContext, "/Index");
redirectContext.RedirectUri = UriHelper.BuildAbsolute(
casUrl.Scheme,
new HostString(casUrl.Host, casUrl.Port),
casUrl.LocalPath, "/logout",
QueryString.Create("service", serviceUrl!));
}
context.Options.Events.RedirectToLogout(redirectContext);
return Task.CompletedTask;
};
})
.AddCAS(o =>
{
o.CasServerUrlBase = builder.Configuration["Authentication:CAS:ServerUrlBase"]; // Set in `appsettings.json` file.
o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.CasValidationUrl = "/";
});
Login controller
[HttpGet("login")]
public IActionResult Login(string returnUrl)
{
// var props = new AuthenticationProperties { RedirectUri = "/" };
return Challenge(new AuthenticationProperties { RedirectUri = returnUrl }, "CAS");
// return Redirect("http://localhost:5095/");
}
[Web Response](https://i.stack.imgur.com/1LFOP.png)
[1]: https://i.stack.imgur.com/v3fKm.png

Related

AddMicrosoftIdentityWebApp with two providers isn't setting IsAuthenticated

I'm trying to get a dual authentication approach working for my .NET6 website. For the front-end, I'm implementing Azure AD B2C, and for the back-end, Azure AD. Here's my code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication()
.AddMicrosoftIdentityWebApp(options => {
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.Instance = "Instance1";
options.TenantId = "TenantId1";
options.ClientId = "ClientId1";
options.ClientSecret = "ClientSecret1";
options.CallbackPath = "/signin-oidc/aadb2b";
options.Scope.Clear();
options.Scope.Add(OpenIdConnectScope.OpenId);
options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.Scope.Add(OpenIdConnectScope.Email);
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
options.MapInboundClaims = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "preferred_username",
ValidateIssuer = false
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = ctx =>
{
ctx.HandleResponse();
ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
return Task.CompletedTask;
};
}, options => {
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
}, "AADB2B.OpenIdConnect", "AADB2B.Cookies");
services.AddAuthentication()
.AddMicrosoftIdentityWebApp(options => {
options.Instance = "Instance2";
options.Domain = "Domain2";
options.TenantId = "TenantId2";
options.ClientId = "ClientId2";
options.ClientSecret = "ClientSecret2";
options.SignUpSignInPolicyId = "USUIP";
options.ResetPasswordPolicyId = "RPP";
options.EditProfilePolicyId = "EPP";
options.CallbackPath = "/signin-oidc/aadb2c";
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "roles"
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = ctx =>
{
ctx.HandleResponse();
ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
return Task.CompletedTask;
};
}, options => {
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
}, "AADB2C.OpenIdConnect", "AADB2C.Cookies");
// Added as an experiment, doesn't seem to help
services.AddAuthorization(options =>
options.DefaultPolicy =
new AuthorizationPolicyBuilder("AADB2B.OpenIdConnect")
.RequireAuthenticatedUser()
.Build());
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseNotFoundHandler();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseGetaCategories();
app.UseGetaCategoriesFind();
app.UseAnonymousId();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/LoginPath", async ctx => ctx.Response.Redirect("/")).RequireAuthorization(authorizeData: new AuthorizeAttribute { AuthenticationSchemes = "AADB2B.OpenIdConnect" });
endpoints.MapGet("/LogoutPath", async ctx => await MapLogout(ctx));
endpoints.MapControllerRoute(name: "Default", pattern: "{controller}/{action}/{id?}");
endpoints.MapControllers();
endpoints.MapRazorPages();
endpoints.MapContent();
});
}
public async Task MapLogout(HttpContext ctx)
{
await ctx.SignOutAsync("AADB2B.OpenIdConnect");
await ctx.SignOutAsync("AADB2B.Cookies");
ctx.Response.Redirect("/");
}
Controller.cs
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string scheme, string returnUrl)
{
return Challenge(new AuthenticationProperties { RedirectUri = string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl });
}
Controller is receiving a hyperlink with the QueryString scheme=AADB2B.OpenIdConnect and scheme=AADB2C.OpenIdConnect respectively.
Upon clicking the hyperlinks, the browser is properly redirected to the signin page for AAD B2C or AAD respectively, and then properly redirected back to the website. A breakpoint in the OnSignedIn event properly shows that the Principal.Identity is indeed a ClaimsIdentity, and IsAuthenticated is true. When arriving in the website, the cookies seem to exist:
However, after the page finishes loading, checking IHttpContextAccessor on subsequent pages shows that the HttpContext.User seems to be a brand-new one, and not the one that exists after the above authentication call.
I tried changing to this:
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalLogin(string scheme, string returnUrl)
{
return Challenge(new AuthenticationProperties { RedirectUri = Url.Action("ExternalLoginCallback", new { scheme = scheme, returnUrl = returnUrl }) }, scheme);
}
[Authorize(AuthenticationSchemes = "AADB2B.OpenIdConnect,AADB2C.OpenIdConnect")]
public async Task<ActionResult> ExternalLoginCallback(string scheme, string returnUrl)
{
var authenticate = await HttpContext.AuthenticateAsync(scheme);
if (authenticate.Succeeded)
User.AddIdentity((ClaimsIdentity)authenticate.Principal.Identity);
return Redirect(string.IsNullOrEmpty(returnUrl) ? "/" : returnUrl);
}
On the authenticate.Succeeded line, I see that my user was properly authenticated. The User.AddIdentity line properly adds the identity to that user. However, when I look on the subsequent page load, the above identity is gone.
I'm at wits end. Any suggestions would be greatly appreciated. Thanks!
Update 1
Navigating directly to a page that is decorated with [Authorize(AuthenticationSchemes = "AADB2C.OpenIdConnect")] DOES properly result in the page recognizing the user as being authenticated. However, from there, navigating anywhere else then shows them no longer being authenticated.
Update 2
Calling IHttpContextAccessor.HttpContext?.AuthenticateAsync("AADB2C.OpenIdConnect") in places where I couldn't decorate with the Authorize flag (due to requiring access for non-authenticated users as well) properly fetches the authenticated user and their information. So, now the only piece of this puzzle I need to solve is finding a way to get Authorize into areas of the code which I can't access, due to being hidden behind proprietary third-party code.
Update 3
I'm unsure why, but it appears as though if I use AddOpenIdConnect instead of AddMicrosoftIdentityWebApp, it ... works? It defaults to that and my back-end now properly recognizes my authentication.
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = null;
options.DefaultSignInScheme = null;
}).AddCookie(options =>
{
options.Events.OnSignedIn = async ctx =>
{
if (ctx.Principal?.Identity is ClaimsIdentity claimsIdentity)
{
// Syncs user and roles so they are available to the CMS
var synchronizingUserService = ctx
.HttpContext
.RequestServices
.GetRequiredService<ISynchronizingUserService>();
await synchronizingUserService.SynchronizeAsync(claimsIdentity);
}
};
}).AddOpenIdConnect(options =>
{
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.Authority = $"MyAuthority";
options.ClientId = "MyClientId";
options.ClientSecret = "MyClientSecret";
options.CallbackPath = "/signin-oidc/aadb2b";
options.Scope.Clear();
options.Scope.Add(OpenIdConnectScope.OpenId);
options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.Scope.Add(OpenIdConnectScope.Email);
options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
options.MapInboundClaims = false;
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "roles",
NameClaimType = "preferred_username",
ValidateIssuer = false
};
options.Events.OnRedirectToIdentityProvider = ctx =>
{
if (ctx.Response.StatusCode == 401)
{
ctx.HandleResponse();
}
return Task.CompletedTask;
};
options.Events.OnAuthenticationFailed = ctx =>
{
ctx.HandleResponse();
ctx.Response.BodyWriter.WriteAsync(Encoding.ASCII.GetBytes(ctx.Exception.Message));
return Task.CompletedTask;
};
});
So, to summarize the steps that I took to resolve this:
Adding [Authorize(AuthenticationSchemes = "MyScheme")] to controllers will properly force authentication when navigating using that controller route.
Calling IHttpContextAccessor.HttpContext?.AuthenticateAsync("MyScheme") returns details of the authenticated principal, allowing code-based control in places where the [Authorize] approach won't work (because it needs to allow both anonymous and authenticated users, and renders differently based on that condition).
For the specific back-end code I couldn't access due to it being hidden behind third-party proprietary code (EPiServer in this case), I was able to resolve the issue by switching to use AddOpenIdConnect instead of AddMicrosoftIdentityWebApp. I'm unsure why this worked, but for the moment I'm not going to question it further.

Both OIDC and ADFS in same application (OWIN)

I am working on an ASP.NET MVC 5 website using EPiServer CMS.
The requirement is that we have admin (back-end users) log in using ADFS (working) and OIDC for front-end.
Both are set to passive mode and are called through action methods in a controller.
The issue I am facing is that front-end users OIDC is set to (Authentication type ="Federation") when it returns from external call instead of external cookie.
public void Configuration(IAppBuilder app)
{
//IdentityModelEventSource.ShowPII = true;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
ConfigureMemberAuth(app);
ConfigureAdminAuth(app);
}
private void ConfigureMemberAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/logmein/"),
LogoutPath = new PathString("/"),
AuthenticationMode = AuthenticationMode.Active
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
_ = app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = ApplicationSettings.OIDCClientId,
ClientSecret = ApplicationSettings.OIDCClientSecret,
Authority = ApplicationSettings.OIDCAuthority,
RedirectUri = $"{ApplicationSettings.Domain}{_oidcRedirectMethod}",
PostLogoutRedirectUri = ApplicationSettings.Domain,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "xxx",
SaveTokens = true,
AuthenticationMode = AuthenticationMode.Passive,
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = notification =>
{
if (string.Equals(notification.ProtocolMessage.Error, "xxx", StringComparison.Ordinal))
{
notification.HandleResponse();
if (string.Equals(notification.ProtocolMessage.ErrorDescription, "xxx", StringComparison.Ordinal))
notification.Response.Redirect(ApplicationSettings.Domain);
else
{
var errorPage = UrlResolver.GetUrl(PageHelper.GetAdminPage().LoginBox.NemIDErrorPage.GetFriendlyUrl());
notification.Response.Redirect(errorPage);
}
}
return Task.CompletedTask;
},
// Retrieve an access token from the remote token endpoint
// using the authorization code received during the current request.
AuthorizationCodeReceived = async notification =>
{
using (var client = new HttpClient())
{
var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);
var tokenEndpointResult = await ExchangeCodeForTokens(notification, client, configuration);
// Add the identity token to the returned ClaimsIdentity to make it easier to retrieve.
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: OpenIdConnectParameterNames.IdToken,
value: tokenEndpointResult.Value<string>(OpenIdConnectParameterNames.IdToken)));
// Retrieve the claims from UserInfo endpoint using the access token as bearer token.
var accesstoken = tokenEndpointResult.Value<string>(OpenIdConnectParameterNames.AccessToken);
var userInfoEndpointResult = await UserInfoEndpointClaims(notification, client, configuration, accesstoken);
//Security note: It is important to verify that the sub claim from ID token matches the sub claim in the UserInfo response
var userinfoSub = userInfoEndpointResult["xx"].Value<string>();
var idtokenSub = notification.AuthenticationTicket.Identity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value;
if (userinfoSub == idtokenSub)
{
//add claims from UserInfo endpoint to identity
foreach (var entry in userInfoEndpointResult)
{
if (!notification.AuthenticationTicket.Identity.HasClaim(c => c.Type == entry.Key))
{
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: entry.Key,
value: entry.Value.ToString()));
}
}
// Add access token to claims.
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
OpenIdConnectParameterNames.AccessToken,
accesstoken));
}
}
},
// Attach the id_token stored in the authentication cookie to the logout request.
RedirectToIdentityProvider = notification =>
{
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var token = notification.OwinContext.Authentication.User?.FindFirst(OpenIdConnectParameterNames.IdToken);
if (token != null)
{
notification.ProtocolMessage.IdTokenHint = token.Value;
}
notification.Response.Redirect(ApplicationSettings.Domain);
}
return Task.CompletedTask;
},
}
});
}
private void ConfigureAdminAuth(IAppBuilder app)
{
//Enable cookie authentication, used to store the claims between requests
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType,
CookieName = WsFederationAuthenticationDefaults.CookieName,
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true,
});
//Enable federated authentication
_ = app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
{
//Trusted URL to federation server meta data
MetadataAddress = ApplicationSettings.MetaDataAddress,
//Value of Wtreal must *exactly* match what is configured in the federation server
Wtrealm = ApplicationSettings.RelyPartyUri,
AuthenticationMode = AuthenticationMode.Passive,
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = (ctx) =>
{
// To avoid a redirect loop to the federation server send 403 when user is authenticated but does not have access
if (ctx.OwinContext.Response.StatusCode == 401 && (ctx.OwinContext.Authentication.User.Identity.AuthenticationType == WsFederationAuthenticationDefaults.AuthenticationType && ctx.OwinContext.Authentication.User.Identity.IsAuthenticated))
{
ctx.OwinContext.Response.StatusCode = 403;
ctx.HandleResponse();
}
//XHR requests cannot handle redirects to a login screen, return 401
if (ctx.OwinContext.Response.StatusCode == 401 && IsXhrRequest(ctx.OwinContext.Request))
{
ctx.HandleResponse();
}
return Task.FromResult(0);
},
SecurityTokenValidated = (ctx) =>
{
//Ignore scheme/host name in redirect Uri to make sure a redirect to HTTPS does not redirect back to HTTP
var redirectUri = new Uri(ctx.AuthenticationTicket.Properties.RedirectUri, UriKind.RelativeOrAbsolute);
if (redirectUri.IsAbsoluteUri)
{
ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.PathAndQuery;
}
//Sync user and the roles to EPiServer in the background
ServiceLocator.Current.GetInstance<ISynchronizingUserService>().SynchronizeAsync(ctx.AuthenticationTicket.Identity);
return Task.FromResult(0);
},
}
});
//Add stage marker to make sure WsFederation runs on Authenticate (before URL Authorization and virtual roles)
app.UseStageMarker(PipelineStage.Authenticate);
// Remap logout to a federated logout
app.Map(LogoutUrl, map =>
{
map.Run(ctx =>
{
ctx.Authentication.SignOut();
ctx.Response.Redirect(ApplicationSettings.Domain);
return Task.FromResult(0);
});
});
}
When I out comment this line: app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
in admin auth then client-login works as intended and authentication type is set to "ExternalCookie" when call return but then admin login stops working ?
Any help would be appreciated

Authorization Code Flow Error in ASP.Net Form using Owin Starup

We are implementing Azure SSO in Traditional ASP.Net Web Application and we want to implement Authorization Code Flow for generating Refresh, Access and Id Tokens.
We have implemented the below code in AuthorizationCodeReceived function of the owin's app.UseOpenIdConnectAuthentication class. From the below mentioned code we are able to successfully fetch the Refreshtoken, AccessToken and IdToken.
But notification.AuthenticationTicket is null and it throws null reference excpetion so we are not able to add the claims for id and access tokens.
Also in the aspx.cs file the HttpContext.Current.User.Identity.IsAuthenticated is returned as false even after generating all the 3 tokens.
Please suggest why notification.AuthenticationTicket is null inside AuthorizationCodeReceived event and what changes we have to do inside AuthorizationCodeReceived event to make HttpContext.Current.User.Identity.IsAuthenticated as "true".
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolValidator dd = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolValidator();
dd.RequireNonce = false;
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
ClientSecret = clientSecret,
PostLogoutRedirectUri = redirectUri,
RedirectUri = redirectUri,
Scope = "openid profile email offline_access",
ResponseType = OpenIdConnectResponseType.Code,
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = (context) =>
{
return System.Threading.Tasks.Task.FromResult(0);
},
AuthorizationCodeReceived = async notification =>
{
using (var client = new HttpClient())
{
var configuration = await notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled);
var request = new HttpRequestMessage(HttpMethod.Post, configuration.TokenEndpoint);
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
[OpenIdConnectParameterNames.ClientId] = notification.Options.ClientId,
[OpenIdConnectParameterNames.ClientSecret] = notification.Options.ClientSecret,
[OpenIdConnectParameterNames.Code] = notification.ProtocolMessage.Code,
[OpenIdConnectParameterNames.GrantType] = "authorization_code",
[OpenIdConnectParameterNames.RedirectUri] = notification.Options.RedirectUri
});
var response = await client.SendAsync(request, notification.Request.CallCancelled);
response.EnsureSuccessStatusCode();
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: OpenIdConnectParameterNames.AccessToken,
value: payload.Value<string>(OpenIdConnectParameterNames.AccessToken)));
notification.AuthenticationTicket.Identity.AddClaim(new Claim(
type: OpenIdConnectParameterNames.IdToken,
value: payload.Value<string>(OpenIdConnectParameterNames.IdToken)));
}
},
// Attach the id_token stored in the authentication cookie to the logout request.
RedirectToIdentityProvider = notification =>
{
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var token = notification.OwinContext.Authentication.User?.FindFirst(OpenIdConnectParameterNames.IdToken);
if (token != null)
{
notification.ProtocolMessage.IdTokenHint = token.Value;
}
}
return Task.CompletedTask;
},
SecurityTokenValidated = (context) =>
{ if (context != null)
{
if (context.ProtocolMessage != null && !string.IsNullOrEmpty(context.ProtocolMessage.IdToken))
{
context.AuthenticationTicket.Identity.AddClaim(new Claim("IdToken", context.ProtocolMessage.IdToken));
}
}
return Task.FromResult(0);
}
}
}
);

HttpContext.Request.IsAuthenticated is false in Ajax request + OpenID Connect Cookie based validation

We are using OpenIdConnect based authentication in the asp.net mvc application. Initial login is working fine. But when we use the Ajax call to invoke the action method, User is coming as not authenticated. I checked in Custom Authorization - HttpContext.Request.IsAuthenticated is coming as false.
I checked the cookie ".AspNet.Cookies" and it has the value. Why is open ID not authenticating the user.
Below is my authentication code
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = AuthenticationConfig.ClientId,
Authority = AuthenticationConfig.AADInstance + AuthenticationConfig.TenantId,
PostLogoutRedirectUri = AuthenticationConfig.PostLogoutRedirectURI,
RedirectUri = AuthenticationConfig.RedirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.Code,
SaveTokens = true,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(AuthenticationConfig.ClientSecret)),
ValidateIssuer = true,
ValidIssuer = AuthenticationConfig.AADInstance + AuthenticationConfig.TenantId + "/v2.0",
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// when an auth code is received...
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
SecurityTokenValidated = async n =>
{
var nid = new ClaimsIdentity(n.AuthenticationTicket.Identity);
//var claimsIdentity = filterContext.HttpContext.User.Identity as ClaimsIdentity;
var user = nid.Claims.Where(r => r.Type == PreferedUserNameClaimType).Select(v => v.Value).FirstOrDefault();
var userRolesroles = GetRolesForUser(user);
//nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));
userRolesroles.ToList().ForEach(ui => nid.AddClaim(new Claim(ClaimTypes.Role, ui)));
// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
// add access token for sample API
nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
n.AuthenticationTicket = new AuthenticationTicket(
nid,
n.AuthenticationTicket.Properties);
UserService.SetUserInformation(user);
},
RedirectToIdentityProvider = ctx =>
{
bool isAjaxRequest = (ctx.Request.Headers != null && ctx.Request.Headers["X-Requested-With"] == "XMLHttpRequest");
if (ctx.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
if (isAjaxRequest && ctx.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
{
ctx.Response.Headers.Remove("Set-Cookie");
ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
ctx.HandleResponse();
return Task.FromResult(0);
}
}
return Task.FromResult(0);
}
}
});
}
Usually in asp.net, the ApiControllers has no concept of your Controller's authentication. Depending on the way things are builr, you need to add an Authorization header with a bearer access token to let the API know about the authenticated user.

How do you get md:NameIDFormat to appear in the SAML2 Metadata output?

I have my SAML2 "working" (authentication: success) but shibboleth isn't sending me any claim data, I need just the users email :)
The shibboleth people are telling me to add this to my SAML2 metadata... it's very clearly not there.
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-
format:emailAddress</md:NameIDFormat>
We're using the OWIN middleware from https://github.com/Sustainsys/Saml2/ to get this all to work, but it's pretty stock config?
additionalProviders["saml2p"] =
(IAppBuilder app, string signInAsType, AuthenticationProviderElement config) =>
{
var opt = new Saml2AuthenticationOptions(false)
{
SPOptions = new SPOptions
{
EntityId = new EntityId("https://my.site.ca")
},
SignInAsAuthenticationType = signInAsType,
AuthenticationType = "saml2p",
Caption = "MySite",
Notifications = new Saml2Notifications()
{
AcsCommandResultCreated = (result, response) =>
{
var claimsIdentity = result.Principal.Identity as ClaimsIdentity;
//None of this exists in the result
var userEmail = claimsIdentity.Claims.FirstOrDefault(x => x.Type == "User.email");
var userFirstName = claimsIdentity.Claims.FirstOrDefault(x => x.Type == "User.FirstName");
var userLastName = claimsIdentity.Claims.FirstOrDefault(x => x.Type == "User.LastName");
},
LogoutCommandResultCreated = commandResult =>
{
// Post logout URL
commandResult.Location = new Uri("/login", UriKind.Relative);
}
},
};
Sustainsys.Saml2.Configuration.Options.GlobalEnableSha256XmlSignatures();
opt.IdentityProviders.Add(new IdentityProvider(
new EntityId("https://their.site.ca/shibboleth-idp/shibboleth"),
opt.SPOptions)
{
LoadMetadata = true
});
app.UseSaml2Authentication(opt);
};
return additionalProviders;
TL;DR; md:NameIDFormat not in SustainSys SAML2 metadata output
The config on my end was correct, the problem was the config in shibboleth not sending the http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier claim back.

Resources