I have this Startup.cs which is taken basically from the MS project template. That should be Authorization Code Flow to authenticate with Azure Active Directory. But it throws an execption with the following message, which says it tries to be a implicit auth flow. But why? How can i force Auth Code Flow? Is there a working example?
I use ASP.NET MVC (NOT Core), 4.xxxx something. The most recent. And Owin.
I get this error: https://login.microsoftonline.com/error?code=700054 and it when i debug, it does not execute "AuthorizationCodeReceived".
Current code:
public void Configuration(IAppBuilder app)
{
string clientId = WebConfigurationManager.AppSettings["ClientId"];
string authority = "https://login.microsoftonline.com/" + WebConfigurationManager.AppSettings["Tenant"] + "/v2.0";
string appKey = WebConfigurationManager.AppSettings["ClientSecret"];
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 = WebConfigurationManager.AppSettings["RedirectUri"],
// PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
PostLogoutRedirectUri = WebConfigurationManager.AppSettings["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 // Simplification (see note below)
},
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(authority, null);
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId).Result;
return Task.FromResult(0);
}
}
}
);
}
private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> arg)
{
throw new NotImplementedException();
}`enter code here`
Related
I have an ASP.NET MVC framework web application. I want to use both .net identity and OpenId Connect for authentication (Microsoft accounts).
It works and redirects to another controller as I want. In this target controller I get information from the claims (which are returned from Azure AD).
What I want is to add more claims to this collection from Azure, or create a new set of claims as I want. I set claims as following but when debugger hits to another controller I see default claims returned by Azure AD only; my modifications are not reflected in the claims collection.
How can I add claims which is usable for both OpenId Connect (Microsoft) and .NET identity authentication?
This is how I set example claims in the controller:
var identity = new ClaimsIdentity("ApplicationCookie", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType);
identity.AddClaim(new Claim("test", "test"));
IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignOut("ApplicationCookie");
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, identity);
This is how I configured in Startup:
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,
// 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,
AuthenticationMode = AuthenticationMode.Passive,
// OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
},
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
RoleClaimType = System.Security.Claims.ClaimTypes.Role,
ValidateIssuer = false
}
});
You have two options. Either hook into the various events provided by AddCookie and AddOpenIDConnect. Or add a custom claims transformation, like:
public class BonusLevelClaimTransformation : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (!principal.HasClaim(c => c.Type == "bonuslevel"))
{
//Lookup bonus level.....
principal.Identities.First().AddClaim(new Claim("bonuslevel", "12345"));
}
return Task.FromResult(principal);
}
}
You also need to register it in Startup.cs like
services.AddTransient<IClaimsTransformation, BonusLevelClaimTransformation>();
We're using IdentityServer4 for our IdentityServer and IdentityServer3 for the client (ASP.NET MVC 5).
Everything works (the User/Claimsprincipal is set correctly through OWIN) except I cannot get the access token from the User.
We're using a implicit client which has access to these scopes: openid, profile, testapi
Startup.cs:
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = identityServerUrl,
RequiredScopes = new[] { "testapi" },
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = identityServerUrl,
ClientId = "testclient",
Scope = "openid profile testapi",
RedirectUri = "http://localhost:49000/signin-oidc",
ResponseType = "id_token token",
SignInAsAuthenticationType = "Cookies",
});
Code to retrieve Access Token (inside one of the controllers):
var user = User as ClaimsPrincipal;
var token = user.FindFirst("access_token");
User is set correctly, but the token is null. I am guessing it is some kind of option that I am missing in the startup.cs, but which?
I think a simpler solution is to use what is allready made availible:
var options = new IdentityServerBearerTokenAuthenticationOptions
{
Authority = authorityUrl,
PreserveAccessToken = true,
};
Then the access token is availible as a claim (named 'token') on the User principle.
I found a solution that does exactly what I want - I'm putting it here for anyone else running into the problem. It costs a dependency on IdentityModel, but that is acceptable in my case:
In Startup.cs, I added:
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var tokenClient = new TokenClient(identityServerUrl + "/connect/token", clientId, secret);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, n.RedirectUri);
HttpContext.Current.Session[HttpUserContext.ACCESS_TOKEN] = tokenResponse.AccessToken;
}
}
To the call to .UseOpenIdConnectAuthentication
I hope anyone is able to help me out with this problem
Im trying to get one of the code samples from the Microsoft Graph Api working with a company specific application. After I sign in at my tenant's sign in screen im getting redirected to the application with the following error.
AADSTS90130: Application '{application id}'
(aad name) is not supported over the /common or /consumers
endpoints. Please use the /organizations or tenant-specific endpoint.
In my startup class i've got the following code:
// The graphScopes are the Microsoft Graph permission scopes that are used by this sample: User.Read Mail.Send
private static string appId = ConfigurationManager.AppSettings["ida:AppId"];
private static string appSecret = ConfigurationManager.AppSettings["ida:AppSecret"];
private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
private static string graphScopes = ConfigurationManager.AppSettings["ida:GraphScopes"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// The `Authority` represents the Microsoft v2.0 authentication and authorization service.
// The `Scope` describes the permissions that your app will need. See https://azure.microsoft.com/documentation/articles/active-directory-v2-scopes/
ClientId = appId,
Authority = "https://login.microsoftonline.com/{tenantid}",
PostLogoutRedirectUri = redirectUri,
RedirectUri = redirectUri,
Scope = "openid email profile offline_access " + graphScopes,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
// In a real application you would use IssuerValidator for additional checks,
// like making sure the user's organization has signed up for your app.
// IssuerValidator = (issuer, token, tvp) =>
// {
// if (MyCustomTenantValidation(issuer))
// return issuer;
// else
// throw new SecurityTokenInvalidIssuerException("Invalid issuer");
// },
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async (context) =>
{
var code = context.Code;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new SessionTokenCache(signedInUserID,
context.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId,
redirectUri,
new ClientCredential(appSecret),
userTokenCache,
null);
string[] scopes = graphScopes.Split(new char[] { ' ' });
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, scopes);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
}
});
}
}
In this code I have the tenant-specific id in the sign in url which works for another application with the same sign-in style.
I'm not sure what is wrong so i'm hoping there is someone who can help me out. I've looked at related questions on here but none seem related to this issue.
You're using the v1 Endpoint to register your application via the Azure Portal and set Multi-tenant to false. This will restrict your application to only AAD users from the tenant at which it's registered.
If you want to accept any AAD user, you'll need to enable multiple tenants. This will allow a report AAD tenant to recognize your application and allow users to authenticate.
If you want to accept both AAD and MSA users, you'll need to register your application at https://apps.dev.microsoft.com. You'll also need to refactor your authentication code to use the v2 Endpoint.
I am in the process of adding Azure-AD integration to an MVC web app. When I run it locally everything is working fine, I can log in, log out and the application functions as expected. However, when I deploy (AWS) and try to access the page I get the following error:
ERR_TOO_MANY_REDIRECTS
I have tried tracing the redirects and it seems that whatever page I go to it redirects (302) to itself in a loop. The login page is never reached and I am unable to access any page even when they are marked to allow anonymous access.
I am already using the app.UseKentorOwinCookieSaver(); fix that is recommended elsewhere but that has had no effect. Any advice would be appreciated.
Per request, this is the authentication code in Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
SetUserInfoCookie(context.AuthenticationTicket.Identity.Name);
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
}
}
});
}
This is the code which calls the authentication when a user clicks on a button. This works as expected when locally, but I am never able to get to the page to run this code after deployment:
[AllowAnonymous]
private ActionResult SignInWithAzureAd(string returnUrl)
{
System.Web.HttpContext.Current.GetOwinContext().Authentication.Challenge();
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext()
.Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
return null;
}
I've set up my web app using OpenIDConnectAuthentication as follows. The OnAuthorizationCodeReceived notification uses Microsoft.IdentityModel.Clients.ActiveDirectory 3.13.8.
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
MetadataAddress = Settings.AADB2CAuth.SignInPolicyMetaAddress, // https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration?p={policy} policy = B2C_1_SignIn
AuthenticationType = Settings.AADB2CAuth.SignInPolicyId, // B2C_1_SignIn
ClientId = Settings.AADB2CAuth.ClientId, // {guid}
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived
},
RedirectUri = Settings.AADB2CAuth.RedirectUri,
Scope = "openid",
ResponseType = "id_token",
});
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential clientCredential = new ClientCredential(Settings.AADB2CAuth.ClientId, Settings.AADB2CAuth.ClientSecret);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst(Settings.ClaimTypes.ObjectIdentifier).Value;
string authority = Settings.AADB2CAuth.Authority; // https://login.microsoftonline.com/{tenant}
AuthenticationContext authContext = new AuthenticationContext(authority, new ADAL.ADALTokenCache(userObjectID));
Uri redirectUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, clientCredential, Settings.AADGraphApi.GraphResourceId);
}
This works fine. However an authorization code is not returned with the id_token. If change this to code id_token or just code, the AuthorizationCodeReceived notification fires, but then I am met with the error
AADSTS70000: Authentication failed: Authorization Code is malformed or invalid
Basically what I'm trying to do is access the B2C AD as the current signed in user. Is this at all possible?
I updated my Authentication Options to
new OpenIdConnectAuthenticationOptions
{
AuthenticationType = Settings.AADB2CAuth.SignInPolicyId,
Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}", Settings.AADB2CAuth.Tenant, Settings.AADB2CAuth.SignInPolicyId),
ClientId = Settings.AADB2CAuth.ClientId,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived
},
RedirectUri = Settings.AADB2CAuth.RedirectUri,
Scope = "openid",
ResponseType = "code id_token",
});
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential clientCredential = new ClientCredential(Settings.AADB2CAuth.ClientId, Settings.AADB2CAuth.ClientSecret);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst(Settings.ClaimTypes.ObjectIdentifier).Value;
string authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}", Settings.AADB2CAuth.Tenant, Settings.AADB2CAuth.SignInPolicyId);
AuthenticationContext authContext = new AuthenticationContext(authority, new ADAL.ADALTokenCache(userObjectID));
Uri redirectUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, redirectUri, clientCredential, Settings.AADGraphApi.GraphResourceId);
}
I am now met with an exception whose details are the HTML content of a 404 page. Looking at the requests I believe it is because AcquireTokenByAuthorizationCodeAsync is looking at https://login.microsoftonline.com/tfp/oauth2/token to send the authorization code to, which I don't think it should?
It may be worth noting that the Authorization Code header I get back is the following:
{
"kid": "cpimcore_09252015",
"ver": "1.0"
}
A quick google search for this yields one result and this references the following issue on the Android ADAL. I'm not sure if this relates to my issue however.
If you look at the beginning of this error:
AADSTSXXXXX
means that when you tried to exchange your auth code, you went to the AAD sts rather than the expected B2C sts:
AADB2CXXXXX
This means your auth code post request was interpreted incorrectly by our endpoint. This is usually caused when the policy (p=B2C_1_xxxx) param for B2C gets appended onto the post URL rather than inside the request.
Option 1:
Refactor your code and library usage to stick the policy param inside the auth code post request rather than the end of the token endpoint URL.
Option 2:
Use the alternate token endpoint and don't tack on any poliy param. Your new endpoint would look like this
https://login.microsoftonline.com/tfp/{tenant}/B2C_1_myB2CPolicy/oauth2/v2.0/token