following Issue 1 and Issue 2 I cant sign out from IS4. Local ASP.NET cookies are not removed and I cannot re-login to IS4.
It works for Core MVC app, but for MVC5 does not.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "TRUX.NET",
Authority = baseAddress,
RedirectUri = $"http://localhost:2510/signin-oidc",
PostLogoutRedirectUri = $"http://localhost:2510/signout-callback-oidc",
ResponseType = "code id_token",
Scope = "openid profile offline_access custom.profile AuthorizationWebApi Common.WebApi",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
SignInAsAuthenticationType = "Cookies",
--------
RedirectToIdentityProvider = n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
Server config
new Client
{
ClientId = "MVC50",
ClientName = "MVC50 APP",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets= {new Secret("123456".Sha256()) },
RedirectUris = { "http://localhost:2510/signin-oidc" },
PostLogoutRedirectUris = {"http://localhost:2510/signout-callback-oidc"},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"custom.profile",
"AuthorizationWebApi",
"Common.WebApi"
},
AllowOfflineAccess = true
}
If any one can help, i'll appreciate.
regards.
Update :
I removed signout-callback-oidc from both server and client configs.
My signout action is this one
public async Task<ActionResult> SignOut()
{
Request.GetOwinContext().Authentication.SignOut();
return Redirect("/");
}
Undeterministically I can call end session endpoint. The logs are given below.
id_token_hint is not null
|1|Microsoft.AspNetCore.Hosting.Internal.WebHost|INFO|Request starting HTTP/1.1 OPTIONS http://www.abcdef.com:5000/connect/endsession?post_logout_redirect_uri=http%3a%2f%2flocalhost%3a2510%2f&id_token_hint=(hint)
|1|Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware|DEBUG|OPTIONS requests are not supported
|0|IdentityServer4.CorsPolicyProvider|DEBUG|CORS request made for path: /connect/endsession from origin: http://localhost:2510 but rejected because invalid CORS path
|9|Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware|DEBUG|AuthenticationScheme: Identity.Application was not authenticated.
|0|IdentityServer4.Hosting.EndpointRouter|DEBUG|Request path /connect/endsession matched to endpoint type EndSession
|0|IdentityServer4.Hosting.EndpointRouter|DEBUG|Mapping found for endpoint: EndSession, creating handler: IdentityServer4.Endpoints.EndSessionEndpoint
|0|IdentityServer4.Hosting.IdentityServerMiddleware|INFO|Invoking IdentityServer endpoint: IdentityServer4.Endpoints.EndSessionEndpoint for /connect/endsession
|0|IdentityServer4.Endpoints.EndSessionEndpoint|WARN|Invalid HTTP method for end session endpoint.
|0|IdentityServer4.Hosting.IdentityServerMiddleware|TRACE|Invoking result: IdentityServer4.Endpoints.Results.StatusCodeResult
|9|Microsoft.AspNetCore.Server.Kestrel|DEBUG|Connection id "0HL3SPFTNB2FH" completed keep alive response.
|2|Microsoft.AspNetCore.Hosting.Internal.WebHost|INFO|Request finished in 1322.3713ms 405
Related
I am trying to implement AuthorizationCode grant type for SwaggerUI that will work with Azure AD.
The error that I am getting is:
Content Security Policy: The page’s settings blocked the loading of a
resource at
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
(“connect-src”)
I used web debugging proxy tool to check all network requests and I see there is a request to my redirect uri with state and authorization code that should be used for token exchange, but that doesn't happen.
My SwaggerUI setup:
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthAppName("My API");
c.OAuthUsePkce();
c.OAuthScopeSeparator(" ");
c.InjectStylesheet("/swagger-ui/dark.css");
c.DisplayRequestDuration();
});
...
string authUrl = $"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/oauth2/v2.0/authorize";
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Description = "OAuth2.0 Auth Code with PKCE",
Name = "oauth2",
Type = SecuritySchemeType.OAuth2,
In = ParameterLocation.Header,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri(authUrl, UriKind.Absolute),
TokenUrl = new Uri(authUrl.Replace("authorize", "token"), UriKind.Absolute),
Scopes = new Dictionary<string, string>
{
{ $"api://{Configuration["AzureAD:ResourceId"]}/user_impersonation", "user impersonation" }
}
}
}
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
},
Array.Empty<string>()
}
});
We are implementing authentication in asp.net mvc application using open id connect.
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = RedirectUri,
ResponseType = OpenIdConnectResponseType.Code,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretkey)),
ValidateIssuer = true,
ValidIssuer = authority,
ValidateAudience = true
// ValidAudience = strAudience
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// when an auth code is received...
AuthorizationCodeReceived = (context) => {
// get the OpenID Connect code passed from Azure AD on successful auth
string code = context.Code;
var handler = new JwtSecurityTokenHandler();
var token = handler.ReadJwtToken(code);
//// successful auth
return Task.FromResult(0);
},
SecurityTokenValidated = (ctx) =>
{
// We can remove claims that are not necessary in this context, mitigating the cookie size.
var identity = ctx.AuthenticationTicket.Identity;
return Task.FromResult(0);
},
AuthenticationFailed = (context) => {
context.HandleResponse();
return Task.FromResult(0);
}
}
});
Authentication is successful, and I am able to get the code.
I am using Authorize attribute in my controller.
Using msal, we received auth token and id_token. I am getting the proper tokens, still after getting tokens, I am getting Infinite loop. I am used UseKentorOwinCookieSaver also. But nothing is working.
The authorization code is not a JWT.
You use it to get JWTs from Azure AD's token endpoint.
You can use MSAL (Microsoft Authentication Library) for this, or make the call yourself.
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
Following the info from Azure Bot Service Authentication I tried to verify the JWT token using the public keys exposed via OpenId:
https://login.botframework.com/v1/.well-known/openidconfiguration
https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
but the key from the directline.botframework.com conversation JWT token is in neither of them, see the error below:
"IDX10501: Signature validation failed. Unable to match key: kid: '...."
ConfigurationManager<OpenIdConnectConfiguration> configurationManager =
new ConfigurationManager<OpenIdConnectConfiguration>(openIdMetadataAddress, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConnectConfiguration = await configurationManager.GetConfigurationAsync(CancellationToken.None);
TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = authorizationDomain,
ValidateAudience = false,
IssuerSigningKeys = openIdConnectConfiguration.SigningKeys
};
try
{
JwtSecurityTokenHandler jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
jwtSecurityTokenHandler.ValidateToken(jwt, tokenValidationParameters, out _);
return true;
}
catch (SecurityTokenException)
{
return false;
}
JWT token example (generated when you start a directline conversation in bot framework):
ew0KICAiYWxnIjogIlJTMjU2IiwNCiAgImtpZCI6ICJBT08tZXhGd2puR3lDTEJhOTgwVkxOME1tUTgiLA0KICAieDV0IjogIkFPTy1leEZ3am5HeUNMQmE5ODBWTE4wTW1ROCIsDQogICJ0eXAiOiAiSldUIg0KfQ.ew0KICAiYm90IjogImRldi1tYXJpdXNpbXBvLW5lcnRlc3Rib3QwbmVnNC1ib3QiLA0KICAic2l0ZSI6ICJ0RVRMM2ZES3ZGdyIsDQogICJjb252IjogIkZPeXRUdThrTzVRNFVOZmxpS3pSMlgtaCIsDQogICJuYmYiOiAxNTc1MzcxNDYzLA0KICAiZXhwIjogMTU3NTM3NTA2MywNCiAgImlzcyI6ICJodHRwczovL2RpcmVjdGxpbmUuYm90ZnJhbWV3b3JrLmNvbS8iLA0KICAiYXVkIjogImh0dHBzOi8vZGlyZWN0bGluZS5ib3RmcmFtZXdvcmsuY29tLyINCn0.IMKMdlart3nEg6iegVvz5MQ86cp36nLXK1mIT0a7xiOmRLMMlvUjqHA9d2EJUovYAML4RGAapP7BWYgU9CnYtL9dXrJwj_JNacJDov18zUTzbyfzcL8goFJG_PJRjJZbN7ZZZdp1lIis9DbrL56HQBgiBuW4BGhNhgmBauh8SFOIvWfhOYmWoxyfI7Uzkd_5LTVdeL7Lyqi5Ulxzf8UsuDI372US6dA0LZ0BZMCU-M6S9bYFCSBwrvjD5uZOYJ8drCuXnuOl1rxRP_kfMVi-kodWZ84-puo5JYt5QhpptP6vuBYO5-6fW359zJ1csUk-xWFlOH88dh09lpJDbcXgXg
using (var client = new DirectLineClient(secretKey))
{
var conversation = await client.Conversations.StartConversationAsync();
var token = conversation.Token;
}
UPD: I'm not sure what the key from the directline.botframework.com conversation JWT token exactly is. If you can provide expired token for me, it should be possible to find out how to validate it.
Metadata endpoint:
https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration
Your code works fine.
Please check out the steps of the test I've done below:
Create a Web App Bot via Azure Portal.
Full description here: https://learn.microsoft.com/en-us/azure/bot-service/abs-quickstart?view=azure-bot-service-4.0
Obtain a token.
Take MICROSOFT-APP-ID and MICROSOFT-APP-PASSWORD from the Configuration of your Web App Bot.
POST https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=MICROSOFT-APP-ID&client_secret=MICROSOFT-APP-PASSWORD&scope=https%3A%2F%2Fapi.botframework.com%2F.default
Come up with values to validate the token.
3.1. Metadata endpoint
Constructed from token endpoint.
https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration
3.2. Issuer
Decoded the token at jwt.io and took actual issuer from there.
https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/
3.3. Audience
Same way as for issuer.
https://api.botframework.com
Validate the token and get ClaimsPrincipal object decoded from the token.
static async Task Main(string[] args)
{
var jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJodHRwczovL2FwaS5ib3RmcmFtZXdvcmsuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiLyIsImlhdCI6MTU3NTkyMDQwMSwibmJmIjoxNTc1OTIwNDAxLCJleHAiOjE1NzU5MjQzMDEsImFpbyI6IjQyVmdZRGhjMDMwNGFrdENBcXZMYTM2aFJTTExBUT09IiwiYXBwaWQiOiI0MmY5NGM0MS0wYmMwLTRiN2MtODc2MC1jOGI1NTRhYjE2NDIiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9kNmQ0OTQyMC1mMzliLTRkZjctYTFkYy1kNTlhOTM1ODcxZGIvIiwidGlkIjoiZDZkNDk0MjAtZjM5Yi00ZGY3LWExZGMtZDU5YTkzNTg3MWRiIiwidXRpIjoiMWpvWi1TUng5a1MwdUxucVYyOE5BQSIsInZlciI6IjEuMCJ9.WWxIinArkAJgVyAUMu6UJvCy9OJ-B2KGxpT-t9wdRF9qlpw00GvXXuL0HCpUEIWC0efA3ETF3bBBJVYjcXoKsC6Up2UWzkAgA2O_TZhPkG5Tkm5MT7f_mIdoEVWoddawjv3ec_EUfSq1B_UrQu-05AHMe0n46kN94yUWbsIAv9z6Q_HSuKO6_kSSyGwbnsAbsT2nWqYyE05BstvZUccQrSvR4UdbugKDEDxAixhVvOrFJiLng3pKeSljXUxWte7ETw59X9EuA4WJPURzW-kWPJ8tGIP2Wz6RVDU-D1eCp-DB3o4PxT-t8UTBMjwUJBFqQo-w1GtQasJwcnUKKkBhgA";
var claimsPrincipal = await Authenticate(jwt);
}
public static async Task<ClaimsPrincipal> Authenticate(string jwt)
{
var openIdMetadataAddress = "https://login.microsoftonline.com/botframework.com/v2.0/.well-known/openid-configuration";
var issuer = "https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/";
var audience = "https://api.botframework.com";
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
openIdMetadataAddress,
new OpenIdConnectConfigurationRetriever());
var openIdConnectConfiguration = await configurationManager.GetConfigurationAsync();
var tokenValidationParameters = new TokenValidationParameters
{
// Updated validation parameters
ValidIssuer = issuer,
ValidAudience = audience,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openIdConnectConfiguration.SigningKeys
};
try
{
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
var claimsPrincipal = jwtSecurityTokenHandler.ValidateToken(jwt, tokenValidationParameters, out _);
return claimsPrincipal;
}
catch (SecurityTokenException e)
{
return null;
}
}
I like to sign out my webapp from an azure ad b2c. I tried the following like suggested in this sample https://www.janaks.com.np/azure-ad-identity-provider-in-aspnet-core-application/.
if (HttpContext.User.Identity.IsAuthenticated)
{
await HttpContext.Authentication.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
await HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
With the following configuration in the Startup.cs:
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = settings.SignInPolicyId,
AutomaticChallenge = true,
CallbackPath = settings.SignInCallbackPath,
ClientId = settings.ClientId,
MetadataAddress = string.Format(settings.AadInstance, settings.Tenant, settings.SignInPolicyId),
PostLogoutRedirectUri = settings.RedirectUri,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
},
AutomaticAuthenticate = true,
Scope = { "openid" },
ResponseType = "id_token",
GetClaimsFromUserInfoEndpoint = true
});
But when I try sign out from the webapp following Exception will be thrown:
InvalidOperationException: No authentication handler is configured to handle the scheme: OpenIdConnect
Thanks for your help.
You have to identify the authentication scheme that you set:
if (HttpContext.User.Identity.IsAuthenticated)
{
await HttpContext.Authentication.SignOutAsync(settings.SignInPolicyId);
await HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
You will somehow have to get the policy id to this controller and use it to identify the appropriate middleware.
The accepted answer is good for Auth 1, but in Auth 2 that method is depreciated, so use the extension method.
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
Reference: https://github.com/aspnet/Announcements/issues/232
I'm currently working on a project where I have a Web API that uses the Graph API to create accounts in my Azure AD and put them in the correct group as well.
Next to that I have several API calls exposed that will be called by a native iOS app as well as an angularJS web app. The client has concocted some weird way of doing the authentication because he firmly believes his users to be utter morons.
The client is handing custom prepped iOS devices to certain people. The device has an Azure AD User(principal) and password. Once they are handed out, some process on the angularJS web app does this, the app will then call my token controller that looks like this:
public async Task<string> GetTokenForUserAsync(string userPrincipalName, string password)
{
var uri = string.Format("{0}/oauth2/token", AuthString);
using (var httpClient = new HttpClient
{
DefaultRequestHeaders = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } }
})
{
var values = new Dictionary<string, string>
{
{"resource", GraphUrl },
{"client_id", ClientId },
{"client_secret", ClientSecret },
{"grant_type", "password" },
{"username", userPrincipalName },
{"password", password },
{"scope", "openid" }
};
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync(uri, content);
var responseContent = await response.Content.ReadAsStringAsync();
return responseContent;
}
The passed parameters are, not 100% exact, but very alike:
AuthString : "https://login.microsoftonline.com/myAzureAD.onmicrosoft.com"
GraphUrl : "https://graph.windows.net"
ClientId : My Web API ClientId (a guid)
ClientSecret : My Web API Client Secret (2year valid access key)
So this call actually provides me with an access_token. The problem that I'm having is that the tokens I get are never authorized. I've tried several Startup setups, but none are working.
Currently I've got the following code in my Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:ClientId"]
}
});
//app.UseWindowsAzureActiveDirectoryBearerAuthentication(
// new WindowsAzureActiveDirectoryBearerAuthenticationOptions
// {
// TokenValidationParameters = new TokenValidationParameters
// {
// ValidAudience = ConfigurationManager.AppSettings["ida:ClientId"]
// },
// Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
// });
}
It's the first time that I'm working with Azure and Active Directory. So I'm probable doing something really stupid. At this moment in time I don't care much about styling and 'the right way'. I just want this thing to work :,/
Hope I described my problem correctly and documented my question accordingly. If you have any questions, please don't hesitate to ask!
Thanks a bunch in advance
I dont think you can get accesstoken from var values = new Dictionary
{
{"resource", GraphUrl },
{"client_id", ClientId },
{"client_secret", ClientSecret },
{"grant_type", "password" },
{"username", userPrincipalName },
{"password", password },
{"scope", "openid" }
};
so you could try other methods:
AuthenticationContext aContext =
new AuthenticationContext("https://login.microsoftonline.com/tenantid");
AuthenticationResult aResult =
aContext.AcquireToken("https://graph.windows.net",
"1950a258-227b-4e31-a9cf-717495945fc2",
new UserCredential(UserId, PasswordId));
string result = string.Empty;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", aResult.AccessToken);
HttpResponseMessage response =
httpClient.GetAsync("https://graph.windows.net/tenantid/users/userid?api-version=1.6").Result;
if (response.IsSuccessStatusCode)
{
result = response.Content.ReadAsStringAsync().Result;
}
Console.WriteLine(result);
Console.ReadLine();