I know there are several threads on the same but none of the solution works for me, is there any solution. The strange thing is when I run the application in IE it works where as in Edge I got into this issue
IDX21323: RequireNonce is '[PII is hidden]'.
OpenIdConnectProtocolValidationContext.Nonce was null,
OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The
nonce cannot be validated. If you don't need to check the nonce, set
OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a
'nonce' is found it will be evaluated.
Here is the code
public class Startup
{
// The Client ID is used by the application to uniquely identify itself to Azure AD.
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
// RedirectUri is the URL where the user will be redirected to after they sign in.
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
// Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
// Authority is the URL for authority, composed by Microsoft identity platform endpoint and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
/// <summary>
/// Configure OWIN to use OpenIdConnect
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the code id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.CodeIdToken,
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
/// <summary>
/// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
}
I tired the options available but nothing works, is there any possible solution for this
Referred to this OpenIdConnectProtocolValidationContext.Nonce was null
Please try updating your Microsoft.Owin.Security.OpenIdConnect package to match the version numbers on your other Owin packages, as discussed in this related thread.
Microsoft.Owin [4.1.1]
Microsoft.Owin.Security [4.1.1]
Microsoft.Owin.Security.Cookies [4.1.1]
Make sure you have also updated to the latest version of Chrome if you are using Google Chrome, or test in other browsers. (This guide discusses an issue with Chrome that was causing this error.)
Related
I am trying to get gain an Access Token after receiving the authorisation token via postback from Azure.
I simply serve the Home view where there is a button to logon to Azure. Initially i followed this post: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp but then tried to modify the code to follow the Authorisation Code Flow.
Strangely it was working nicely (after googling for days) when hosted on IIS Express but when moving to local IIS I am only able to complete half the flow. I wonder if it's significant that my app is running as an Application on IIS not as a website? The address is https://localhost/testapp. My site on IIS runs on port 80 and 443 as standard. IIS is as per (apologies for the heavy redaction):
In essence the code redemption is not taking place.
Here is my owin Startup.cs:
string clientId = ConfigurationManager.AppSettings["ClientId"];
string clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
string redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
static string tenant = ConfigurationManager.AppSettings["Tenant"];
string authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, ConfigurationManager.AppSettings["Authority"], tenant);
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
ClientSecret = clientSecret,
Authority = authority,
RedirectUri = redirectUri, //struggling to see the difference between RedirectUri and CallbackPath
CallbackPath = new PathString("/home/"), // do i need this as well?
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
RedeemCode = true,
ResponseMode = OpenIdConnectResponseMode.FormPost, // do i need this?
SaveTokens = true, // do i need this?
UsePkce = true, // default is true
ResponseType = OpenIdConnectResponseType.Code,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
},
}
);
}
My home controller is simply:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
[AllowAnonymous]
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
/// <summary>
/// Send an OpenID Connect sign-out request.
/// </summary>
[AllowAnonymous]
public void SignOut()
{
HttpContext.GetOwinContext().Authentication.SignOut(
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
}
web.config:
<add key="ClientId" value="xxxxxxxxxxxx" />
<add key="ClientSecret" value="xxxxxxxxxxxx" />
<add key="redirectUri" value="https://localhost/testapp/home/" />
<add key="Tenant" value="xxxxxxxxxxxx" />
<add key="Authority" value="https://login.microsoftonline.com/{0}/v2.0" />
On Azure the Redirect URI is:
this is the trace from fiddler:
You can see from the trace that there is not further call to the token endpoint even though I believe I have the correct configuration for OWIN to make the call automatically?
I am really stumped and I would appreciate some help please.
I am trying to get Owin working in an asp.net web app with Azure AD. Here is my startup code that configures Owin.
public class Startup
{
// The Client ID is used by the application to uniquely identify itself to Microsoft identity platform.
string clientId = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
// RedirectUri is the URL where the user will be redirected to after they sign in.
string redirectUri = System.Configuration.ConfigurationManager.AppSettings["RedirectUri"];
// Tenant is the tenant ID (e.g. contoso.onmicrosoft.com, or 'common' for multi-tenant)
static string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
// Authority is the URL for authority, composed by Microsoft identity platform endpoint and the tenant name (e.g. https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0)
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
/// <summary>
/// Configure OWIN to use OpenIdConnect
/// </summary>
/// <param name="app"></param>
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Sets the ClientId, authority, RedirectUri as obtained from web.config
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = redirectUri,
Scope = OpenIdConnectScope.OpenIdProfile,
// ResponseType is set to request the id_token - which contains basic information about the signed-in user
ResponseType = OpenIdConnectResponseType.IdToken,
// ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false // This is a simplification
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
MessageReceived = OnMessageReceived
}
}
);
}
/// <summary>
/// Handle failed authentication requests by redirecting the user to the home page with an error in the query string
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
context.HandleResponse();
context.Response.Redirect("/?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
private Task OnMessageReceived(MessageReceivedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
string tmp = notification.ProtocolMessage.ErrorDescription;
return Task.FromResult(0);
}
}
This code does not work unless I enable implicit flow in Azure AD. I don't want to use implicit flow because of security reasons. I would rather use authorization flow. What changes do I need to make, so that this code will work without enable implicit flow in Azure?
According to your code, you use OpenID Connect protocol to integrate Azure AD auth in your application. If so, the app registration in the AD portal must have the implicit grant of id_tokens enabled in the Authentication tab (which sets the oauth2AllowIdTokenImplicitFlow flag in the application manifest to true). So that users can successfully request an ID token from the /authorization endpoint. If we do not do that, we will get the unsupported_response error. For more details, please refer to here.
I was tasked with writing an ASP.NET website that uses Azure Active Directory. I went with the route of OAuth and OpenID Connect. I am not able to use implicit flow and therefore must set the ResponseType to be code.
Using MSAL code samples I got most of it working but the problem is that all the samples are using a response type that returns tokens. I think I need to do it in 2 separate steps, first get the authorization code and then get the id token. I'm not exactly sure how to do this and would much appreciate some guidance here.
I have a startup class that look like this:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions { });
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
Authority = authority,
ClientId = clientId,
RedirectUri = redirectUri,
Scope = "openid profile email offline_access user.readbasic.all", // a basic set of permissions for user sign in & profile access
ResponseType = OpenIdConnectResponseType.Code,
ClientSecret = clientSecret,
TokenValidationParameters = new TokenValidationParameters
{
// In a real application you would use ValidateIssuer = true for additional checks and security.
ValidateIssuer = false,
NameClaimType = "name",
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
}
});
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
{
// Handle any unexpected errors during sign in
context.OwinContext.Response.Redirect("/Error?message=" + context.Exception.Message);
context.HandleResponse(); // Suppress the exception
return Task.FromResult(0);
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
/*
The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
Azure AD and has a full set of claims.
*/
IConfidentialClientApplication confidentialClient = GroupManager.Utils.MsalAppBuilder.BuildConfidentialClientApplication(null);
// Upon successful sign in, get & cache a token using MSAL
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(new[] { "openid profile email offline_access user.readbasic.all" }, context.Code).ExecuteAsync();
}
How do I take the information from the result's tokens and create a claims identity for the AuthenticationTicket.Identity and access the user info?
Please note that this is an ASP.NET application. Not MVC and not Core.
If you use MSAL, you don't need to handle the code yourself. MSAL will return the token to you after you log in interactively, please see:Overview of Microsoft Authentication Library (MSAL).
Before that, you need to take a look at Add sign-in to Microsoft to an ASP.NET web app,the workflow is:
Code example please check: https://github.com/AzureAdQuickstarts/AppModelv2-WebApp-OpenIDConnect-DotNet
Update:
Try to enable ID token
Well, this was a mistake.
I decided to migrate my MVC5 application to MVC6 and things were going fine until I needed to migrate my authentication.
My MVC application was logging in using an external Web Api 2 application which returns a token.
I built a filter to handle that very simply like this:
/// <summary>
/// Uses the session to authorize a user
/// </summary>
public class SimpleAuthorize : AuthorizeAttribute
{
/// <summary>
/// Authorizes the user
/// </summary>
/// <param name="httpContext">The HTTP Context</param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var accessToken = httpContext.Session["AccessToken"];
if (accessToken == null)
return false;
return true;
}
}
which was applied to all controllers.
Now, it appears that you can't do that anymore as mentioned here.
So, how can I get my application to work with the API?
I have tried searching and found nothing that helps me with my situation. Does anyone know how I can solve this or could point me in the direction of some decent documentation?
You'd approach it by writing Authorization middleware which creates an identity out of the access token. Having a valid identity is enough for authorization to succeed, and then you can delve into policy should you need something more detailed. Something like
public class SessionAuthenticationHandler :
AuthenticationHandler<SessionAuthenticationOptions>
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var sessionToken = Request.HttpContext.Session.GetString("Token");
if (sessionToken == null)
{
return AuthenticateResult.Failed("No session token");
}
// Construct principal here
var principal =
new ClaimsPrincipal(new ClaimsIdentity(new[] {
new Claim("SessionToken", sessionToken) }, Options.AuthenticationScheme));
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(),
Options.AuthenticationScheme);
return AuthenticateResult.Success(ticket);
}
}
Having said that the reason ASP.NET has never used session to hold authentication details is due to session fixation attacks.
I need to authenticate User from a central CAS. The assumption are these:
The UserId for authentication is in a Request Header
The roles for authorization are given by a web service.
The application must cache the authorization phase.
I've tried this:
In the Global.asax:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
const string SiteMinderHeaderToken = "SM_USER";
if (HttpContext.Current.User == null || !HttpContext.Current.User.Identity.IsAuthenticated)
{
var userSSO = HttpContext.Current.Request.Headers[SiteMinderHeaderToken];
GenericIdentity webIdentity = new GenericIdentity(userSSO, "SiteMinder");
string[] roles = { "ROLE1", "ROLE2" };
GenericPrincipal principal = new GenericPrincipal(webIdentity, roles);
HttpContext.Current.User = principal;
// System.Web.Security.FormsAuthentication.SetAuthCookie(userSSO, true);
}
}
In the Web.config
<authentication mode="None" />
<authorization>
<deny users="?" />
</authorization>
The problem is that for every request, the HttpContext.Current.User is always null, and every time all the authentication and authorization phase are done.
If I uncomment
System.Web.Security.FormsAuthentication.SetAuthCookie(userSSO, true);
All is fine, after the first request the User is authenticated.
My questions are:
Is it correct to call System.Web.Security.FormsAuthentication.SetAuthCookie even if there isn't FormAuthentication?
Is there a way to do it better?
Are there some security issues doing this way?
Thanks
FormsAuthentication in this case is just storing the users session.
Here's some more info: Understanding the Forms Authentication Ticket and Cookie https://support.microsoft.com/en-us/kb/910443/
You can set the auth cookie this way, but if would like to store more of the users info within the session, I would recommend using claims.
FormsAuthentication cookie is secure. "The ticket is encrypted and signed using the configuration element of the server's Machine.config file. ASP.NET 2.0 uses the decryptionKey and the new decryption attribute of the element to encrypt forms authentication tickets." There's more information in the link I gave above. I guess the only security issue would be how the user is accessing your code "FormsAuthentication.SetAuthCookie(userSSO, true);"
Try looking into Owin Authentication to store the users session.
http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
Here's an example of how I use it in my MVC project.
Installed nugget packages:
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security.OAuth
Microsoft.Owin.Security.Cookies
Created OwinConfig.cs file (in my App_Start folder):
using Microsoft.Owin;
[assembly: OwinStartup(typeof(MyProject.OwinConfig))]
namespace MyProject
{
using System.Web.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;
/// <summary>
/// The startup.
/// </summary>
public class OwinConfig
{
/// <summary>
/// The configuration.
/// </summary>
/// <param name="app">
/// The app.
/// </param>
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = FormsAuthentication.FormsCookieName,
Provider = new CookieAuthenticationProvider()
});
}
}
}
Code I use to log a user in:
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, 1)),
new Claim("CustomClaim", "My bio or something"),
new Claim(ClaimTypes.Name, "set_username")
};
var userWithClaims = new ClaimsIdentity(claims, FormsAuthentication.FormsCookieName);
var owinContext = HttpContext.Current.Request.GetOwinContext();
var authenticationManager = owinContext.Authentication;
authenticationManager.SignIn(userWithClaims);
And then I access the stored claims by creating IdentityExtensions.cs class (also in my app_start folder):
public static class IdentityExtensions
{
public static string GetCustomClaim(this IIdentity identity)
{
if (identity == null)
{
throw new ArgumentNullException("identity");
}
var ci = identity as ClaimsIdentity;
return ci != null ? ci.FindFirstValue("CustomClaim") : null;
}
private static string FindFirstValue(this ClaimsIdentity identity, string claimType)
{
if (identity == null)
{
throw new ArgumentNullException("identity");
}
var claim = identity.FindFirst(claimType);
return claim != null ? claim.Value : null;
}
}
So in my controller I could just call:
string customClaim = this.HttpContext.User.Identity.GetCustomClaim();
Then finally, to logout I would use:
var owinContext = HttpContext.Current.Request.GetOwinContext();
var authenticationManager = owinContext.Authentication;
authenticationManager.SignOut();