Session Times out after 20 mins using Identity Server Hybrid Flow with Cookie Authentication - asp.net-core

I am fairly certain I am missing some configuration.
I am testing a new app using Identity Server 4 with a ASP.NET core 1.0 website in a hybrid flow configured as below.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "Cookies",
Authority = "https://account.testsite.com",
RequireHttpsMetadata = true,
ClientId = "superId",
ClientSecret = "supersecretclient",
ResponseType = "code id_token",
Scope = { "api1", "offline_access", "profile", "openid" },
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
}
});
Users keep getting logged out after 20 mins irrespective of their activity on the website.
Client config for IdSrv4 as follows:
Absolute refresh time: 2592000
Access token lifetime: 3600
Authorization code lifetime: 300
Identity token lifetime: 300
Sliding refresh token lifetime: 1296000
If the user hits refresh on the page, he is promptly logged back in automatically, however, the page has auto load features which fails.

I haven't tried with the official ASP Identity feature set, but I am implementing a third-party sign-on using Cookies.
There are a range of available options for the Cookie Authentication method. My example below is from my current application. The key ones are SlidingExpiration and ExpireTimeSpan
app.UseCookieAuthentication(new CookieAuthenticationOptions() {
CookieName = "Company.MyApp.Web." + env.EnvironmentName.ToLower(),
AuthenticationScheme = "MyAppCookieAuth",
LoginPath = new PathString("/Home/Login/"),
AccessDeniedPath = new PathString("/Home/AccessDenied/"),
AutomaticAuthenticate = true,
AutomaticChallenge = true,
ExpireTimeSpan = TimeSpan.FromHours(2),
SlidingExpiration = true
});
So when the Cookie is half expired, on the next page Request it will be refreshed. This may help persist their login for a longer period.
More information on Cookie Authentication:
Using Cookie Middleware without ASP.NET Core Identity -
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie
Introduction to Identity (contains sections pertaining to cookies) - https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity

Related

Automatic login using jwt in Cookie in ASP.net MVC Core

My process flow is :
User logs into a Issuer Application (Username/Password)
Clicks a link of the Client Application that they want to goto
Issuer Application creates a jwt and stores it in a Cookie
Issuer Application does a Response.Redirect to Client Application
Client Application authenticates user using the jwt in the Cookie and creates the Principal and automatically logs in user.
Below is my Client Application setting from the Startup ConfigureServices method:
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("password"));
SigningCredentials SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidIssuer = "issuerapp",
ValidateAudience = false,
ValidAudience = "clientapp",
ValidateIssuerSigningKey = true,
IssuerSigningKey = SigningCredentials.Key,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.ClaimsIssuer = "issuerapp";
options.TokenValidationParameters = tokenValidationParameters;
options.SaveToken = true;
})
.AddCookie(JwtBearerDefaults.AuthenticationScheme,
options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.Name = Configuration.GetValue<string>("AppSettings:CookieName");
options.AccessDeniedPath = authenticationSettings.AccessDeniedPath;
options.Events = new CookieAuthenticationEvents
{
// Check if JWT needs refreshed
OnValidatePrincipal = RefreshTokenMonitor.ValidateAsync,
OnSigningOut = (context) =>
{
context.HttpContext.Response.Redirect(Configuration.GetValue<string>("AppSettings:LogoutPath"));
return Task.CompletedTask;
},
};
});
In my Client Application I have all controllers decorated with [Authorize] attribute.
I need the Client Application automatically authenticating the user using the jwt. Which is not happening using the above mentioned configurations.
My AccessDeniedPath (Action Method) is not getting hit either.
The workaround that I have been using is to redirect user from the Issuer Applicaiton to a Login action in the Client Application where :
I read the jwt from the Cookie
Validate the jwt to get the Principal
Call httpContext.SignInAsync
How can I get the user logged in automatically using the jwt.
Any help / pointer are appreciated.
Thanks.
By default , the AddJwtBearer extension will get the token from request's Authorization header :
Authorization: Bearer <token>
But you are pass the token in cookie , so you can find the token in cookie and set token in OnMessageReceived event in AddJwtBearer :
options.Events = new JwtBearerEvents {
OnMessageReceived = ctx =>
{
ctx.Token = ctx.HttpContext.Request.Cookies["jwt"];
return Task.CompletedTask;
}
};

Keycloak backchannel login with .NET core api?

According to this OpenID spec https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html
OpenID Connect Client Initiated Backchannel Authentication Flow is an authentication flow like OpenID Connect. However, unlike OpenID Connect, there is direct Relying Party to OpenID Provider communication without redirects through the user's browser. This specification has the concept of a Consumption Device (on which the user interacts with the Relying Party) and an Authentication Device (on which the user authenticates with the OpenID Provider and grants consent). This specification allows a Relying Party that has an identifier for a user to obtain tokens from the OpenID Provider. The user starts the flow with the Relying Party at the Consumption Device, but authenticates and grants consent on the Authentication Device.
I'm trying to do the same with my .NET core api using Keycloak as the IdentityProvider. This is current working setup with Authorization Code Flow
var oidcOptions = new OpenIdConnectAuthenticationOptions()
{
AuthenticationType = OpenIdConnectAuthenticationDefaults.AuthenticationType,
ClientId = Application.Config.KeycloakConfig.Default.ClientId,
Authority = Application.Config.KeycloakConfig.Default.Authority,
RedirectUri = Application.Config.KeycloakConfig.Default.SiteManagementAuthRedirectUri,
ClientSecret = Application.Config.KeycloakConfig.Default.ClientSecret,
RequireHttpsMetadata = false,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Scope = "openid",
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
RequireExpirationTime = true,
IssuerValidator = (issuer, securityToken, validationParameters) =>
{
if (issuer != Application.Config.KeycloakConfig.Default.Authority)
throw new SecurityTokenInvalidIssuerException("Invalid issuer");
return issuer;
},
ValidAudience = Application.Config.KeycloakConfig.Default.ClientId
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
RedirectToIdentityProvider = OnRedirectToIdentityProvider
}
};
appBuilder.UseOpenIdConnectAuthentication(oidcOptions);
I managed to get the tokens using password grant as below
HTTP POST {{KeyCloakUrl}}/realms/My-Realms/protocol/openid-connect/token
{
grant_type: "password",
client_id: "Keycloak_client_id",
client_secret: "***",
username: "username i created in Keycloak",
password: "***",
response_type : "code id_token",
scope: "openid"
}
Reponse:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI***********",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJ********",
"token_type": "bearer",
"id_token": "eyJhbG**********",
"not-before-policy": 0,
"session_state": "c2803e20-bc04-4a9c-9b1a-d1fd3dfd1f22",
"scope": "openid email profile"
}
Question: Is it possible to manual make a POST request with id_token to the .NET core api to authenticate the requests and how to do it?

asp.net mvc login from identityserver4 with a invalid_request error

today i use the demo of identityserver4 Build a validation server, and i can use the asp.net core client with openid login the client.
but i could not login my asp.net mvc5 client with openid, The error of the prompt is : invalid_request,
here is my identityserver4 config code with getclient()
// clients want to access resources (aka scopes)
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
// OpenID Connect hybrid flow and client credentials client (MVC)
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = true,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
}
};
}
}
and the follow code is my asp.net mvc5 clent ConfigureAuth(),because the idenetiyServer4 define the ClientSecrets is "secret".Sha256(),so in this mvc client , i set the ClientSecret = GetSHA256HashFromString("secret"),i create prvate the method GetSHA256HashFromString() to convert the string to sha256.
here is my code:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "Cookies",
Authority = "http://localhost:5000", //ID Server SSO Server
ClientId = "mvc",
ClientSecret = GetSHA256HashFromString("secret"),
ResponseType = "code id_token",
RedirectUri = "http://localhost:5002/signin-oidc", //URL of Client website
PostLogoutRedirectUri = "http://localhost:5002/signout-callback-oidc", //URL of Client website
Scope = "api1",
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
RequireHttpsMetadata = false,
});
and i press f5 to run the mvc client ,and press the button of login,the brower can jump to the localhost:5000,but it is give me a error:
Sorry, there was an error : invalid_request and the other error info are :
Request Id: 0HL9RHBTJIT3T:00000003**
thanks a lot.
The value of ClientSecret should be the actual secret value not the hashed one.
The secret is stored as hash when you use a persisted data storage to prevent an attacker to obtain your client's secrets in case if your storage is compromised.
In your case, The secret value is "secret". So the code will be
ClientSecret = "secret"

"Unexpected code_verifier" when trying to achieve authorization in hybrid flow (with PKCE) using IdentityServer4

I'm trying to implement native clients (.NET Console applications as a mockup at first) to authenticate using OpenID Connect against IdentityServer4 as my STS. I use IdentityModel.OidcClient2 as my client library.
I chose to implement the code based authentication flow.
I am able to pass through the authentication stage but when I get to the authorization stage I get an error message at the client saying
invalid_grant
At the IdentityServer the error message is
"Unexpected code_verifier: XXXXXXXXXXX...."
Even though when I open fiddler and look at the requests and the debug info - the code verifier sent to the IdentityServer for the authorization seems as the client generated at first in the AuthorizationState class.
If I execute with AuthorizationState.CodeVerifier = null then it works.
But I do want to implement the PKCE for extra security. How can I achieve that?
Here is the configuration of that specific client
Identity Server :
new Client
{
ClientId = "nativeapp1",
ClientName = "Native App Demo - 1",
AllowedGrantTypes = GrantTypes.Hybrid,
RequireConsent = true,
ClientSecrets =
{
new Secret("some-secret1".Sha256())
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"custom.name",
"api1"
},
RedirectUris = {"http://127.0.0.1:7890/"},
//PostLogoutRedirectUris = {"" }
AllowOfflineAccess = true
}
And the client configuration
var options = new OidcClientOptions
{
Authority = _authority,
ClientId = "nativeapp1",
RedirectUri = redirectUri,
Scope = "openid profile api1 custom.name offline_access",
FilterClaims = true,
LoadProfile = false,
Flow = OidcClientOptions.AuthenticationFlow.Hybrid,
ClientSecret = "some-secret1"
};
You need to set RequirePkce to true on you client configuration in IdentityServer.

Why ASP.NET Core adds claims twice into User.Claims property?

I have asp.net core application and the application is using OpenIdConnect authentication using IdentityServer3. When the user is authenticated successfully the application receives proper claims from identity server. I can debug the line TokenValidatedContext.Ticket.Principal.Claims in OnTokenValidatd and make sure application receives all the claims.
Code Snippet
var connectOptions = new OpenIdConnectOptions()
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = authority,
ClientId = clientId,
ResponseType = IdentityConstant.IdTokenClaim,
AuthenticationScheme = IdentityConstant.OpenIdAuthenticationScheme,
SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
PostLogoutRedirectUri = postlogoutRedirectUri,
CallbackPath = IdentityConstant.CallbackPath,
Events = new OpenIdConnectEvents()
{
OnTokenValidated = async context =>
{
var claims = context.Ticket.Principal.Claims;
await Task.FromResult(0);
}
}
};
below is the quick watch of TokenValidatedContext.Ticket.Principal.Claims in OnTokenValidated handler
However, after successful authentication when I debug User.Cliams in Home controller, I see all the claims are added twice.
Below is the quick watch of User.Claims in Home controller
Why the claims are getting added twice in User.Claims?
Because you set openidconnect's AutomaticAuthenticate to true. If you look user identities you will see there are two identities(One for cookie other for openidconnect authentication). Since User.Claims are sum of these identity claims, you see claims twice. So, removing AutomaticAuthenticate = true, from openidconnect options solves the problem.