AddOpenIdConnect with Azure AD V2 - azure-active-directory

Having some issues with AddOpenIdConnect in net core 2.0 and Azure AD (V2). After challenging, entering credentials in Azure AD and being returned back to my application the authenticationhandler seems to redirect me to the original method that initiated the challenge rather than the defined callback method.
However, the httpcontext.user is populated with a claims identity with the correct claims on it.
Code is simplified for the purpose of this post.
Startup looks like:
services.AddAuthentication(o =>
{
o.DefaultChallengeScheme = "aad";
o.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, o =>
{
if (!Environment.IsProduction())
{
o.RequireHttpsMetadata = false;
}
o.Authority = Configuration.GetValue<string>("Authentication:Authority");
o.Audience = Constants.Audiences.Self;
o.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role,
IssuerSigningKey =
new X509SecurityKey(
GetSigningCertificate(Configuration.GetValue<string>("Certificates:Signing")))
};
})
.AddCookie()
.AddOpenIdConnect("aad", o =>
{
if (!Environment.IsProduction())
{
o.RequireHttpsMetadata = false;
}
o.Authority = "https://login.microsoftonline.com/{tenantID}/v2.0";
o.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
o.ClientId = "[clientid]";
o.ClientSecret = "[clientsecret]";
o.ResponseMode = "form_post";
o.ResponseType = "id_token";
o.CallbackPath = new PathString("/api/connect/microsoftcallback2");
o.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://login.microsoftonline.com/{tenantID}/v2.0"
};
});
Challenge:
[AllowAnonymous]
[HttpGet("authorize", Name = "authorize")]
public async Task<IActionResult> ChallengeTemp()
{
return Challenge("aad");
}
Trace log:
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST https://localhost:44301/api/connect/microsoftcallback2 application/x-www-form-urlencoded 1771
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST https://localhost:44301/api/connect/microsoftcallback2 application/x-www-form-urlencoded 1771
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Trace: Entering Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler's HandleRemoteAuthenticateAsync.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Trace: Entering Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler's HandleRemoteAuthenticateAsync.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLC9LOBLM019", Request id "0HLC9LOBLM019:00000004": started reading request body.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLC9LOBLM019", Request id "0HLC9LOBLM019:00000004": started reading request body.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLC9LOBLM019", Request id "0HLC9LOBLM019:00000004": done reading request body.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLC9LOBLM019", Request id "0HLC9LOBLM019:00000004": done reading request body.
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing unprotect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler', 'aad', 'v1').
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing unprotect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler', 'aad', 'v1').
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Trace: MessageReceived: '?id_token={keyremoved}'.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Trace: MessageReceived: '?id_token={keyremoved}'.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Debug: Updating configuration
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Debug: Updating configuration
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Debug: Received 'id_token'
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Debug: Received 'id_token'
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing unprotect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler', 'System.String', 'aad', 'v1').
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing unprotect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler', 'System.String', 'aad', 'v1').
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing protect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware', 'Cookies', 'v2').
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing protect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware', 'Cookies', 'v2').
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies signed in.
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies signed in.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLC9LOBLM019" completed keep alive response.
Microsoft.AspNetCore.Server.Kestrel:Debug: Connection id "0HLC9LOBLM019" completed keep alive response.
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 129.6921ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 129.6921ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET https://localhost:44301/api/connect/authorize
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET https://localhost:44301/api/connect/authorize
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing unprotect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware', 'Cookies', 'v2').
Microsoft.AspNetCore.DataProtection.KeyManagement.KeyRingBasedDataProtector:Trace: Performing unprotect operation to key {keyremoved} with purposes ('workspace', 'Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware', 'Cookies', 'v2').
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies was successfully authenticated.
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies was successfully authenticated.
Microsoft.AspNetCore.Routing.Tree.TreeRouter:Debug: Request successfully matched the route with name 'authorize' and template 'api/connect/authorize'.
Microsoft.AspNetCore.Routing.Tree.TreeRouter:Debug: Request successfully matched the route with name 'authorize' and template 'api/connect/authorize'.

If you look at your log carefully, you'll see that it says this:
Request starting HTTP/1.1 POST https://localhost:44301/api/connect/microsoftcallback2
Since you specified the response should come as a POST with:
o.ResponseMode = "form_post";
You get the response as a POST in the callback you defined.
Now by default the OIDC handler will redirect you back to the path you were trying to access when the challenge was issued. You can see the 302 redirect in the log too.
In the case of an explicit login handler, that is not good.
You should define it yourself with:
return Challenge(new AuthenticationProperties
{
RedirectUri = "/"
}, "aad");
In this case the user will be redirected to the root of the app after the login is processed.

Related

Service account request to IAP-protected app results in 'Invalid GCIP ID token: JWT signature is invalid'

I am trying to programmatically access an IAP-protected App Engine Standard app via Python from outside of the GCP environment.
I have tried various methods, including the method shown in the docs here: https://cloud.google.com/iap/docs/authentication-howto#iap-make-request-python. Here is my code:
from google.auth.transport.requests import Request
from google.oauth2 import id_token
import requests
def make_iap_request(url, client_id, method='GET', **kwargs):
"""Makes a request to an application protected by Identity-Aware Proxy.
Args:
url: The Identity-Aware Proxy-protected URL to fetch.
client_id: The client ID used by Identity-Aware Proxy.
method: The request method to use
('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE')
**kwargs: Any of the parameters defined for the request function:
https://github.com/requests/requests/blob/master/requests/api.py
If no timeout is provided, it is set to 90 by default.
Returns:
The page body, or raises an exception if the page couldn't be retrieved.
"""
# Set the default timeout, if missing
if 'timeout' not in kwargs:
kwargs['timeout'] = 90
# Obtain an OpenID Connect (OIDC) token from metadata server or using service
# account.
open_id_connect_token = id_token.fetch_id_token(Request(), client_id)
print(f'{open_id_connect_token=}')
# Fetch the Identity-Aware Proxy-protected URL, including an
# Authorization header containing "Bearer " followed by a
# Google-issued OpenID Connect token for the service account.
resp = requests.request(
method, url,
headers={'Authorization': 'Bearer {}'.format(
open_id_connect_token)}, **kwargs)
print(f'{resp=}')
if resp.status_code == 403:
raise Exception('Service account does not have permission to '
'access the IAP-protected application.')
elif resp.status_code != 200:
raise Exception(
'Bad response from application: {!r} / {!r} / {!r}'.format(
resp.status_code, resp.headers, resp.text))
else:
return resp.text
if __name__ == '__main__':
res = make_iap_request(
'https://MYAPP.ue.r.appspot.com/',
'Client ID from IAP>App Engine app>Edit OAuth Client>Client ID'
)
print(res)
When I run it locally, I have the GOOGLE_APPLICATION_CREDENTIALS environment variable set to a local JSON credential file containing the keys for the service account I want to use. I have also tried running this in Cloud Functions so it would presumably use the metadata service to pick up the App Engine default service account (I think?).
In both cases, I am able to generate a token that appears valid. Using jwt.io, I see that it contains the expected data and the signature is valid. However, when I make a request to the app using the token, I always get this exception:
Bad response from application: 401 / {'X-Goog-IAP-Generated-Response': 'true', 'Date': 'Tue, 09 Feb 2021 19:25:43 GMT', 'Content-Type': 'text/html', 'Server': 'Google Frontend', 'Content-Length': '47', 'Alt-Svc': 'h3-29=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"'} / 'Invalid GCIP ID token: JWT signature is invalid'
What could I be doing wrong?
The solution to this problem is to exchange the Google Identity Token for an Identity Platform Identity Token.
The reason for the error Invalid GCIP ID token: JWT signature is invalid is caused by using a Google Identity Token which is signed by a Google RSA private key and not by a Google Identity Platform RSA private key. I overlooked GCIP in the error message, which would have told me the solution once we validated that the token was not corrupted in use.
In the question, this line of code fetches the Google Identity Token:
open_id_connect_token = id_token.fetch_id_token(Request(), client_id)
The above line of code requires that Google Cloud Application Default Credentials are setup. Example: set GOOGLE_APPLICATION_CREDENTIALS=c:\config\service-account.json
The next step is to exchange this token for an Identity Platform token:
def exchange_google_id_token_for_gcip_id_token(google_open_id_connect_token):
SIGN_IN_WITH_IDP_API = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp'
API_KEY = '';
url = SIGN_IN_WITH_IDP_API + '?key=' + API_KEY;
data={
'requestUri': 'http://localhost',
'returnSecureToken': True,
'postBody':'id_token=' + google_open_id_connect_token + '&providerId=google.com'}
try:
resp = requests.post(url, data)
res = resp.json()
if 'error' in res:
print("Error: {}".format(res['error']['message']))
exit(1)
# print(res)
return res['idToken']
except Exception as ex:
print("Exception: {}".format(ex))
exit(1)
The API Key can be found in the Google Cloud Console -> Identity Platform. Top right "Application Setup Details". This will show the apiKey and authDomain.
More information can be found at this link:
Exchanging a Google token for an Identity Platform token

Why doesn't "AutomaticRedirectAfterSignOut = true;" work for redirecting "post-logout" in the following context?

The main application is a ASP.NET Core 2.2 Web Application using Razor Pages.
User accesses the application URL & gets redirected to IdentityServer4 login screen
User successfully login
User Logsout and gets redirected to "You have been logged out screen" of IdentityServer4 after successfully skipping the logout confirmation prompt.
This step fails: The user is NOT automatically redirected to the login page of IdentityServer4. He is instead redirected to a page where he is informed that he has logged off, here he is also asked if he wants to go to the login page, which is where i want him to be automatically redirected to without any prompts.
What I have tried so far:
In IDP >> Quickstart >> Account >> AccountOptions >> public static bool AutomaticRedirectAfterSignOut = true; (this should be regarded as set to true the entire post)
In IDP >> Startup >> ConfigureServices >> .AddOpenIdConnect >>
options.SignedOutCallbackPath = "/signout-callback-oidc"; Adding this line of code did not help.
I tried running using Chrome, Firefox, Edge - the issue persisted
Code from IDP Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// adds MVC framework services
services.AddMvc();
/*// adds services Razor Pages and ASP.NET MVC require
.AddRazorPagesOptions(options => {options.Conventions.AddPageRoute("/index", "home"); });*/
// dependency injection of services
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // registers an IHttpContextAccessor so we can access the current HttpContext in services by injecting it
services.AddScoped<IThisHttpClient, ThisHttpClient>(); // registers an "ThisHttpClient"
services.AddScoped<IOrderService, OrderService>();
services.AddScoped<IAccountService, AccountService>();
services.AddLogging();
// adding open id connect authentication
services
.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies") // configures the cookie handler and enables the application to use cookie based authentication for the default scheme
// The below handler creates authorization requests, token and other requests, and handles the identity token validation
// "oidc" ensure when part of application requires authentication, "OpenIdConnect" will be triggered by default
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies"; // matches the default scheme for authentication, ensures that succesful authentication result will be stored in a our applications "cookie"
options.Authority = "https://localhost:44370/"; // the authority is set to be the identity provider (the authority responsible for the IDP part of the OIDC flow), the middleware will use this value to read the metadata on the discovery end point, so it knows where to find different endpoints ad other information
options.ClientId = "3TL"; // must match client ID at IDP level
options.ResponseType = "code id_token"; // one of response type of the Hybrid ground to be used
//options.CallbackPath = new PathString("..."); // allows the change of the redirect Uri from inside the IdentityServerConfig (hand made class)
//options.SignedOutCallbackPath = new PathString("...") //
options.Scope.Add("openid"); // required scope which requires in sub value being included
options.Scope.Add("profile"); // ensures profile related claims are included
options.Scope.Add("roles"); // requesting a CUSTOM MADE scope (check IDP scopes/roles for details)
options.SaveTokens = true; // allows the middleware to save the tokens it receives from the identity provider so we can easelly use them afterwards
options.ClientSecret = "test_secret"; // must match secret at IDP level
options.GetClaimsFromUserInfoEndpoint = true; // enables GETing claims from user info endpoint regarding the current authenticate user
//options.ClaimActions.Remove("amr"); // allows us to remove CLAIM FILTERS (AKA this ensures the AMR(Authentication Method Reference) claim is dispalyed and not filtered out)
options.ClaimActions.DeleteClaim("sid"); // removing unnecessary claims from the initial cookie (session ID at level of IDP)
options.ClaimActions.DeleteClaim("idp"); // removing unnecessary claims from the initial cookie (the Identity Provider)
options.ClaimActions.DeleteClaim("name"); // removing unnecessary claims from the initial cookie (removing this type of data reduces the cookie size)
options.ClaimActions.DeleteClaim("given_name"); // removing unnecessary claims from the initial cookie (removing this type of data reduces the cookie size)
options.ClaimActions.DeleteClaim("family_name"); // removing unnecessary claims from the initial cookie (removing this type of data reduces the cookie size)
options.ClaimActions.MapUniqueJsonKey("role", "role"); // adding the CUSTOM MADE claim to the claims map
//options.SignedOutCallbackPath = "/signout-callback-oidc"; // NOTE THIS DOES NOT WORK
});
}
Code from IDP Config.cs
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
/// IMPORTANT: The client details declared in here must be matched with their exact copy on the client
new Client
{
ClientId = "3TL",
ClientName = "3 Tier Logistics",
//using hybrid flow to authenticate users
AllowedGrantTypes = GrantTypes.Hybrid,
// limits the URIs the user can be redirected to after getting authenticated or logging out
RedirectUris = {"https://localhost:44321/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:44321/signout-callback-oidc" },
AllowedScopes = // configures the allowed scopes for this particular client (aka what user info to share from all the available)
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"roles"
},
ClientSecrets =
{
new Secret("test_secret".Sha256())
}
}
};
}
Code from Application Server Logout Page
public class LoginModel : PageModel
{
private OrderService orderService;
private string targetUrlRegisterClient;
[BindProperty]
public Client Client { get; set; }
public void OnGet()
{
Task.Run(() => HttpContext.SignOutAsync("Cookies"));
Task.Run(() => HttpContext.SignOutAsync("oidc"));
}
public void OnPost()
{
targetUrlRegisterClient = "http://localhost:8080/server_war_exploded/root/api/registerclient";
orderService = new OrderService();
Task<string> response = orderService.PostRegisterClientAsync(Client, targetUrlRegisterClient);
}
}
Debug output of logout hit
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:44321/Login
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Route matched with {page = "/Login"}. Executing page /Login
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing handler method Client_Customer.Pages.LoginModel.OnGet with arguments ((null)) - ModelState is Valid
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed handler method OnGet, returned result .
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executing an implicit handler method - ModelState is Valid
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Information: AuthenticationScheme: oidc signed out.
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: Cookies signed out.
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker:Information: Executed page /Login in 69.7362ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/connect/endsession?post_logout_redirect_uri=https%3A%2F%2Flocalhost%3A44321%2Fsignout-callback-oidc&id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IjA5MmI0Yjk0MjQzNjJiZmQ3ZWM3Y2MyMDU1NGFiMTZlIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NTc5MDkzNzAsImV4cCI6MTU1NzkwOTY3MCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzNzAiLCJhdWQiOiIzVEwiLCJub25jZSI6IjYzNjkzNTA2MTYwMzY3NjQyMC5ZMkl6WTJReVlqRXRZMlF5TXkwME5UTmlMVGhpWkdFdFlqQTBNRGsxTVRWak1qazRNak5rTVdaaU1XRXRNV1EwTXkwME0yVTBMVGsxTlRNdFlUTmpZalEwWlRVd1pHSm0iLCJpYXQiOjE1NTc5MDkzNzAsImF0X2hhc2giOiJWWHVYRHJZcTZNWnF3X2N2T0h3eDNnIiwic2lkIjoiNGViNzQ5ZjA5ZGQ4MjNkNzI5NmQzMjU1NWU5MGJiMDYiLCJzdWIiOiIyMjIyIiwiYXV0aF90aW1lIjoxNTU3OTA5MzY3LCJpZHAiOiJsb2NhbCIsImFtciI6WyJwd2QiXX0.NzqA4kILvZgjlTd6dhku6827dG-_9MkJpAH11inQ0-biR0GXP7fkrklIRy8DgxDh8zEriNMUSM8gd9E_p7Zn4hn-HRZ5MJf1hOHfyo3Pdih0sgZ6eNzOvAManiLgNb85n6hcNx04H7PRLHjlZOR01dYkjZrnRCNTWLnVlrsu3xmnonagOtvtF5a_QuZqVJvUedqxby95RH-U5AuqW2pdPTQfzQVZBvUXrAdJGj6wOXwHCn9TSpRJcH4OPtWOMvP8Z84Iiz8vH_lK_qtBUkcSmjs_kOt_qFeGYgDE_xv71HMa0HhcbJlQ-GPwTJu2cA0teGUby33Sj-td92A7y1v5mQ&state=CfDJ8IMSTeB9liZHhYIais0HVw5svLoMCzrej-fgkjkCV_TaQjqMXAXfoVdkgkWNdpnCfCNjv9hXQ_qcU3uSC7KVbJaFghyxVZD1b3eL8Yeb_G8gnDDGoJYODAljLU_pki5M9aZbR_UbjmpgodcofaWnccPgRlLOf3nSTH1eiS2zoe8n&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.3.0.0
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 94.0857ms 302 text/html; charset=utf-8
IdentityServer4.Hosting.EndpointRouter:Debug: Request path /connect/endsession matched to endpoint type Endsession
IdentityServer4.Hosting.EndpointRouter:Debug: Endpoint enabled: Endsession, successfully created handler: IdentityServer4.Endpoints.EndSessionEndpoint
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.EndSessionEndpoint for /connect/endsession
IdentityServer4.Endpoints.EndSessionEndpoint:Debug: Processing signout request for 2222
IdentityServer4.Validation.EndSessionRequestValidator:Debug: Start end session request validation
IdentityServer4.Validation.TokenValidator:Debug: Start identity token validation
IdentityServer4.Stores.ValidatingClientStore:Debug: client configuration validation for client 3TL succeeded.
IdentityServer4.Validation.TokenValidator:Debug: Client found: 3TL / 3 Tier Logistics
IdentityServer4.Test.TestUserProfileService:Debug: IsActive called from: IdentityTokenValidation
IdentityServer4.Validation.TokenValidator:Debug: Calling into custom token validator: IdentityServer4.Validation.DefaultCustomTokenValidator
IdentityServer4.Validation.TokenValidator:Debug: Token validation success
{
"ClientId": "3TL",
"ClientName": "3 Tier Logistics",
"ValidateLifetime": false,
"Claims": {
"nbf": 1557909370,
"exp": 1557909670,
"iss": "https://localhost:44370",
"aud": "3TL",
"nonce": "636935061603676420.Y2IzY2QyYjEtY2QyMy00NTNiLThiZGEtYjA0MDk1MTVjMjk4MjNkMWZiMWEtMWQ0My00M2U0LTk1NTMtYTNjYjQ0ZTUwZGJm",
"iat": 1557909370,
"at_hash": "VXuXDrYq6MZqw_cvOHwx3g",
"sid": "4eb749f09dd823d7296d32555e90bb06",
"sub": "2222",
"auth_time": 1557909367,
"idp": "local",
"amr": "pwd"
}
}
IdentityServer4.Validation.EndSessionRequestValidator:Information: End session request validation success
{
"ClientId": "3TL",
"ClientName": "3 Tier Logistics",
"SubjectId": "2222",
"PostLogOutUri": "https://localhost:44321/signout-callback-oidc",
"State": "CfDJ8IMSTeB9liZHhYIais0HVw5svLoMCzrej-fgkjkCV_TaQjqMXAXfoVdkgkWNdpnCfCNjv9hXQ_qcU3uSC7KVbJaFghyxVZD1b3eL8Yeb_G8gnDDGoJYODAljLU_pki5M9aZbR_UbjmpgodcofaWnccPgRlLOf3nSTH1eiS2zoe8n",
"Raw": {
"post_logout_redirect_uri": "https://localhost:44321/signout-callback-oidc",
"id_token_hint": "***REDACTED***",
"state": "CfDJ8IMSTeB9liZHhYIais0HVw5svLoMCzrej-fgkjkCV_TaQjqMXAXfoVdkgkWNdpnCfCNjv9hXQ_qcU3uSC7KVbJaFghyxVZD1b3eL8Yeb_G8gnDDGoJYODAljLU_pki5M9aZbR_UbjmpgodcofaWnccPgRlLOf3nSTH1eiS2zoe8n",
"x-client-SKU": "ID_NETSTANDARD2_0",
"x-client-ver": "5.3.0.0"
}
}
IdentityServer4.Endpoints.EndSessionEndpoint:Debug: Success validating end session request from 3TL
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 60.2054ms 302
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/Account/Logout?logoutId=CfDJ8IMSTeB9liZHhYIais0HVw7kc9GyTnHxoDNbRDCp7qA3cjwUvTmwOc8iOz2QeBz21IjWognFICseoXkQwH-eQJtXY9bWxa1vP3GLUz98cWKI1VMcnArspUbxRT9bcUF1ZrmloS7t_Us-uV9Ipa-VkgSUmLZfXAWa2f-WXPIv3VRmGwLNV7dqQa_pQOTkyDoiW_ddGElcwit15bJ3BLS6f52dYIU5fzjlSBtzGqT516-usiS-wmfacbACtJQn1VaEahKiBnW7X1gI4PRhQhCZF-IFzeXjESuMigwFyUay7K79DOZCqJ7ReU-RZ7GR1TsFsnqS8212Dr10hkznljRMnDeB6CZbB87LorJxZvf_eH33NBhzJzCZ1bwvPoz_vJeQoHO50P1IfNUGZjO8Y7pYimUC52SCe0jCKUUF8a5t_HZHMNVvtoCgC8b42zHE9rM5ms25BWLTFsgQH6wJFG09fmI5Eu_ICWCTm7XbQxMsBLK8cXdyIb_g1ccqaoz1gohMtpfciokB5_xInN1EcResbtkRUNeLO5DN_c5aFX5QZrC-HJVqxLAdKzZ4coL-x06s8Emvu9w3S1ZjlYLZCPMKHfK1LKgAFnqq1rUEV9PDtwmWDe_gz9ga45MHyMYrrFTswq2ut2gylVFbnb9nEt-g4OWzC6Mqi1mq6Y9CmWefYDwmbsyO-hM9r-p3bZVgYCJkw77zP0UDzFndpM_82gwMau8PH85qxEk1Hz3kJJxilbvAOX1lowfBBymXZH1M0qaiIVS0V2MPL19ySkhIiBQZs7rYcsXMU-wILzhm_729Xt9TxUwZxRFgwngE2fFizgyG_g
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Route matched with {action = "Logout", controller = "Account"}. Executing action IdentityServer4.Quickstart.UI.AccountController.Logout (IdentityServer)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method IdentityServer4.Quickstart.UI.AccountController.Logout (IdentityServer) with arguments (CfDJ8IMSTeB9liZHhYIais0HVw7kc9GyTnHxoDNbRDCp7qA3cjwUvTmwOc8iOz2QeBz21IjWognFICseoXkQwH-eQJtXY9bWxa1vP3GLUz98cWKI1VMcnArspUbxRT9bcUF1ZrmloS7t_Us-uV9Ipa-VkgSUmLZfXAWa2f-WXPIv3VRmGwLNV7dqQa_pQOTkyDoiW_ddGElcwit15bJ3BLS6f52dYIU5fzjlSBtzGqT516-usiS-wmfacbACtJQn1VaEahKiBnW7X1gI4PRhQhCZF-IFzeXjESuMigwFyUay7K79DOZCqJ7ReU-RZ7GR1TsFsnqS8212Dr10hkznljRMnDeB6CZbB87LorJxZvf_eH33NBhzJzCZ1bwvPoz_vJeQoHO50P1IfNUGZjO8Y7pYimUC52SCe0jCKUUF8a5t_HZHMNVvtoCgC8b42zHE9rM5ms25BWLTFsgQH6wJFG09fmI5Eu_ICWCTm7XbQxMsBLK8cXdyIb_g1ccqaoz1gohMtpfciokB5_xInN1EcResbtkRUNeLO5DN_c5aFX5QZrC-HJVqxLAdKzZ4coL-x06s8Emvu9w3S1ZjlYLZCPMKHfK1LKgAFnqq1rUEV9PDtwmWDe_gz9ga45MHyMYrrFTswq2ut2gylVFbnb9nEt-g4OWzC6Mqi1mq6Y9CmWefYDwmbsyO-hM9r-p3bZVgYCJkw77zP0UDzFndpM_82gwMau8PH85qxEk1Hz3kJJxilbvAOX1lowfBBymXZH1M0qaiIVS0V2MPL19ySkhIiBQZs7rYcsXMU-wILzhm_729Xt9TxUwZxRFgwngE2fFizgyG_g) - Validation state: Valid
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler:Information: AuthenticationScheme: idsrv signed out.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action method IdentityServer4.Quickstart.UI.AccountController.Logout (IdentityServer), returned result Microsoft.AspNetCore.Mvc.ViewResult in 24.8267ms.
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor:Information: Executing ViewResult, running view LoggedOut.
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor:Information: Executed ViewResult - view LoggedOut executed in 19.1822ms.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action IdentityServer4.Quickstart.UI.AccountController.Logout (IdentityServer) in 48.1906ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 59.083ms 200 text/html; charset=utf-8
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/lib/bootstrap/css/bootstrap.css
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/icon.png
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/lib/jquery/jquery.js
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 17.4182ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 16.464ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/css/site.css
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 20.1358ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 8.4111ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/lib/bootstrap/js/bootstrap.js
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/js/signout-redirect.js
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/connect/endsession/callback?endSessionId=CfDJ8IMSTeB9liZHhYIais0HVw4UP5MHpGg4UIrZ1rPYhDNKZ0T8aLNc6sQ00tDxQN7898mdUQGymNjElfE09nHu53Jcmj2OlrmLZdqwrS33_ea8BVUC1KpYuh1NtSAGTbqHF-Z4GVqWLIM3--4-kv1Jwggs2PBPjytq65cjCge00Zg2lNQEsKjgNxupv-gNwSWvdklOEQ9gRuGAd8dTXhUJqHomK7a87OWqQvuE1hQieeDesgtCSaVhC9-CcaEJycYkOkmyrxFrOWG4Npw6smPd-XU
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 7.3577ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 7.7551ms 404
IdentityServer4.Hosting.EndpointRouter:Debug: Request path /connect/endsession/callback matched to endpoint type Endsession
'iisexpress.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.4\System.Net.Security.dll'. Symbols loaded.
IdentityServer4.Hosting.EndpointRouter:Debug: Endpoint enabled: Endsession, successfully created handler: IdentityServer4.Endpoints.EndSessionCallbackEndpoint
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.EndSessionCallbackEndpoint for /connect/endsession/callback
IdentityServer4.Endpoints.EndSessionCallbackEndpoint:Debug: Processing signout callback request
IdentityServer4.Stores.ValidatingClientStore:Debug: client configuration validation for client 3TL succeeded.
IdentityServer4.Validation.EndSessionRequestValidator:Debug: No client front-channel logout URLs
IdentityServer4.Validation.EndSessionRequestValidator:Debug: No client back-channel logout URLs
IdentityServer4.Endpoints.EndSessionCallbackEndpoint:Information: Successful signout callback.
IdentityServer4.Endpoints.EndSessionCallbackEndpoint:Debug: No client front-channel iframe urls
IdentityServer4.Endpoints.EndSessionCallbackEndpoint:Debug: No client back-channel iframe urls
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 103.2685ms 200 text/html; charset=UTF-8
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/favicon.ico
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 1.9017ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/favicon.ico
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 2.2642ms 404
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44370/favicon.ico
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 2.4325ms 404
The core problem:
I want to be redirected to the login page of the IDP which i can not currently achieve, and i need help to understand what am I doing wrong.
P.S. I am 3rd semester at IT University and this is the first time I am using IndetityServer4, I hope to use it for all my future applications, thank you for your patience.
If you're looking for the signout-redirect.js:
window.addEventListener("load", function () {
var a = document.querySelector("a.PostLogoutRedirectUri");
if (a) {
window.location = a.href;
}
});

User is not getting authenticated (cookies not getting set) after SAML getting processed successfully

I am using an idp initiated SSO flow. I am using Kentor.AuthServices using OWIN middleware.
Most of the flow works except, user identity is not getting SET when the control reaches my callback method after successfully processing the SAML response.
Setting in web.config:
<kentor.authServices entityId="https://one-staging.com/MVSAMLServiceProvider"
returnUrl="https://5814a15e.ngrok.io/api/Account/UnsolicitedExternalLogin">
<identityProviders>
<add entityId="https://shibidp.edu/idp/shibboleth"
metadataLocation = "~/Providers/SAML2/Metadata/shibidp.edu.xml"
allowUnsolicitedAuthnResponse="false"
disableOutboundLogoutRequests="false"
binding="HttpRedirect">
</add>
<add entityId="abb:one:saml20:idp"
metadataLocation="~/Providers/SAML2/Metadata/abb.xml"
allowUnsolicitedAuthnResponse="true"
disableOutboundLogoutRequests="false"
binding="HttpRedirect">
</add>
</identityProviders>
</kentor.authServices>
Here is my Startup.cs:
public void ConfigureOAuth(IAppBuilder app)
{
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth2/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat()
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
googleAuthOptions = new GoogleOAuth2AuthenticationOptions()
{
ClientId = System.Configuration.ConfigurationManager.AppSettings["GoogleClientId"],
ClientSecret = System.Configuration.ConfigurationManager.AppSettings["GoogleClientSecret"],
Provider = new GoogleAuthProvider()
};
app.UseGoogleAuthentication(googleAuthOptions);
app.Use(async (Context, next) =>{await next.Invoke();});
app.UseKentorAuthServicesAuthentication(CreateSAMLAuthServicesOptions());
app.Use(async (Context, next) =>{await next.Invoke();});
}
Here are the Kentor logs (no errors in the logs):
DEBUG 2018-12-28 14:02:32,682 8859ms emv-authService-logger MoveNext - Received unsolicited Saml Response _t0r6DHtsGygxkYcfNzdkEs72.M which is allowed for idp abb:one:saml20:idp
DEBUG 2018-12-28 14:02:32,729 8906ms emv-authService-logger MoveNext - Signature validation passed for Saml Response _t0r6DHtsGygxkYcfNzdkEs72.M
DEBUG 2018-12-28 14:02:32,729 8906ms emv-authService-logger MoveNext - Extracted SAML assertion oN4v.k9x2GE7s5S8OdeNWS.93j9
DEBUG 2018-12-28 14:02:32,729 8906ms emv-authService-logger MoveNext - Validated conditions for SAML2 Response _t0r6DHtsGygxkYcfNzdkEs72.M
INFO 2018-12-28 14:02:32,729 8906ms emv-authService-logger ProcessResponse - Successfully processed SAML response _t0r6DHtsGygxkYcfNzdkEs72.M and authenticated 10035094
Finally my redirect method:
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ApplicationCookie)]
[AllowAnonymous]
[Route("UnsolicitedExternalLogin", Name = "UnsolicitedExternalLogin")]
public async void GetUnsolicitedExternalLogin()
{
bool isAuthenticated = User.Identity.IsAuthenticated; //getting false
}
I have unfortunately been stuck with this problem for a week now. I'm sure this is really close to getting done, so any help would be greatly appreciated.
Thanks!
Looking at the code, I think that there is a mismatch on authentication schemes.
In the pipeline setup, a cookie middleware for the external authentication scheme is setup. But in the GetUnsolicitedExternalLogin method, the ApplicationCookie scheme is referenced. Change it to reference the external scheme instead.
It is also a good idea to check if the redirect from ~/AuthServices/Acs to GetUnsolicitedExternalLogin sets an external authentication cookie.

Spring Integration : Connection Failure vs Error response code retryadvice()

i'm sending a request to a web service through Http.outboundGateway, and i 'm expecting to have a response with on of these three cases
1- Response success [Ok]
2- Connection Failure [ need to Retry]
3- response return with error code, ex. 400 [save it]
i used advice() after poller to reattempt the Connection Failure, but The problem is that the Error Message exception was thrown in both cases ( Connection Failure , response error code ), so the retry was called for both cases
How could i differentiate between of them and only use the Retry advice for the Connection Failure
.handle(
Http.outboundGateway(propertiesConfig.getURL())
......
, endpoint -> endpoint
.poller(Pollers.fixedDelay(delayBetweenRequests)
.errorChannel("errorChannel")
.taskExecutor(executor)
.receiveTimeout(timeoutDelay)
)
.advice(retryAdvice)
)
Retry advice creation bean
#Bean("retryAdvice")
public RequestHandlerRetryAdvice maspRetryAdvice() {
Request
HandlerRetryAdvice retryAdvice = new RequestHandlerRetryAdvice();
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy policy = new FixedBackOffPolicy();
policy.setBackOffPeriod(interval);
retryTemplate.setBackOffPolicy(policy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(nRetry);
retryTemplate.setRetryPolicy(retryPolicy);
retryAdvice.setRetryTemplate(retryTemplate);
ErrorMessageSendingRecoverer recover = new
ErrorMessageSendingRecoverer(aggregatorChannel());
handlerRetryAdvice.setRecoveryCallback(recover);
return retryAdvice;
}
I was facing almost the exact same challenge.
For Connection time out this link provides the exact solution: [Configure error handling and retry for Http.outboundGateway spring dsl
] [1]: Configure error handling and retry for Http.outboundGateway spring dsl
....
.handle(Http.outboundGateway(
parser().parseExpression("headers[url]"))
.httpMethod(HttpMethod.POST)
.headerMapper(headerMapper())
.expectedResponseType(String.class)
.requestFactory(clientHttpRequestFactory())
// The inner writer method is doing nothing, just place holder for future usage,
// errorHandler is necessary to capture :
// e.g. org.springframework.web.client.HttpClientErrorException: 403 Forbidden
.errorHandler(responseErrorFileWriter())
, (Consumer<GenericEndpointSpec>)e -> e.advice(retryAdvice()))

How to configure IdentityServerAuthenticationOptions.Authority to use wildcards

I successfully setup IdentityServer4 with ASP.NET Core.
As a default config I had this:
IdentityServerAuthenticationOptions options = new IdentityServerAuthenticationOptions()
{
Authority = "http://localhost:5000",
ScopeName = "scope",
ScopeSecret = "ScopeSecret",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false,
};
Now, using this guide I configured to be read from configuration files and so they can be any numbers in production.
For example if I setup API to be running at http://*:5000 then the client can connect to it via the service IP address like http://192.168.1.100:5000.
Once the client obtains the Bearer token and tries to use it, an Internal Server Error occures with this exception:
Unable to obtain configuration from:
'http://*:5000/.well-known/openid-configuration'.
---> System.IO.IOException: IDX10804: Unable to retrieve document from: 'http://*:5000/.well-known/openid-configuration'.
---> System.UriFormatException: Invalid URI: The hostname could not be parsed.
What is the correct way to configure IdS4 to have dynamic authority?
Update
It seems the problem is with Issuer, any idea on this?
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException:
IDX10205: Issuer validation failed. Issuer: 'http://192.168.1.100:5000'. Did not match: validationParameters.ValidIssuer: 'http://localhost:5000' or validationParameters.ValidIssuers: 'null'.
at Microsoft.IdentityModel.Tokens.Validators.ValidateIssuer(String issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
By a big surprise, all I needed, was to set a value (almost any value) for IssuerUri:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
////...
var identiyBuilder = services.AddIdentityServer(options =>
{
options.RequireSsl = false;
options.IssuerUri = "MyCompany";
});
////...
}
Now, by the above config, I can use the service by any IP address.
I didn't find I could just put in MyCompany
But in my log files I had the following:
Bearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'https://crm.example.com'. Did not match: validationParameters.ValidIssuer: 'MyCompany' or validationParameters.ValidIssuers: 'null'.
I don't quite know what 'issuer' means but I was able to just take 'https://crm.example.com' and get things working with this :
options.IssuerUri = "https://crm.example.com";

Resources