Keycloak backchannel login with .NET core api? - asp.net-core

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?

Related

Invalid Grant error after being redirected to client application from OpenIdDict application

I have an application using OpenIdDict Beta 6 with ASP.NET Core 5.
I am using OpenIdDict Velusia sample and I have the following client:
OpenIddictApplicationDescriptor application = new OpenIddictApplicationDescriptor {
ClientId = "spa",
ClientSecret = "secret",
ConsentType = OpenIddictConstants.ConsentTypes.Implicit,
PostLogoutRedirectUris = {
new Uri("https://localhost:5002/oidc/signout")
},
RedirectUris = {
new Uri("https://localhost:5002/oidc/signin"),
},
Permissions = {
OpenIddictConstants.Permissions.Endpoints.Authorization,
OpenIddictConstants.Permissions.Endpoints.Logout,
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,
OpenIddictConstants.Permissions.GrantTypes.RefreshToken,
OpenIddictConstants.Permissions.ResponseTypes.Code,
OpenIddictConstants.Permissions.Scopes.Email,
OpenIddictConstants.Permissions.Scopes.Profile,
OpenIddictConstants.Permissions.Prefixes.Scope + "api"
},
Requirements = {
OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange
}
};
On my client application I am using OIDC Client and I have the following settings:
const settings: UserManagerSettings = {
automaticSilentRenew: true,
authority: "https://localhost:5000",
client_id: 'spa',
client_secret: "secret",
filterProtocolClaims: true,
loadUserInfo: true,
post_logout_redirect_uri: "https://localhost:5000/oidc/signout",
redirect_uri: "https://localhost:5000/oidc/signin",
response_mode: 'query',
response_type: 'code',
scope: 'openid profile email api'
};
I am able to login with email/password and to logout ...
When I login with Google I am redirected back to OpenIdDict application.
I get the successful login message and asked to confirm email to create the account.
I confirm it and I get redirected back to the spa client application ...
But when the OIDCClient-JS, in the spa, calls "/connect/token" I get the error:
Error: invalid_grant
It was working with previous OpenIdDict version ...
What am I missing?

ID Token in Resource Owner Password Grant in IdentityServer4

I have implemented IdentityServer 4 in a .Net Core API project.
I created a Resource Owner Credentials Grant client and allowed openID scopes.
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.Ids)
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients).
AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>();
Here's my client code:
new Client
{
ClientId = "resourceownerclient",
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AccessTokenType = AccessTokenType.Jwt,
AlwaysSendClientClaims = true,
Enabled = true,
ClientSecrets= new List<Secret> { new Secret("Secret".Sha256()) },
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"api1"
}
}
I have also added all the scopes in AddInMemoryIdentityResources().
But the response does not contain ID token.
{
"access_token": "eyJhbGciOiJSUzI1NiIsImt",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "api1 openid"
}
Looked at the other question on the SO on whether the Open ID Connect supports the Resource Owner Credentials Grant.
How can I get ID token in the response?
The refresh token call returns ID token but it's a bug.

Auth0 - Authenticating with RS256 using JWT on Owin with bearer-accessToken

While implementing Auth0 Authentication/Authorization with a normal embedded login, I am able to authenticate the user and gets back the valid accessToken/idToken.
Initialization
webAuth = new auth0.WebAuth({
domain: 'xxx.auth0.com',
clientID: 'myclientid',
responseType: 'token id_token'
});
Successfully getting token.
webAuth.client.login({
realm: _Connection,
username: 'aaa#b.com',
password: 'password',
audience: 'https://xxx.auth0.com/api/v2/',
scope: 'openid profile email'
}, function (err, args) {
if (!err)
{
webAuth.client.userInfo(token, function (args, authUserData) {
var ajaxAdapter = breeze.config.getAdapterInstance("ajax");
***Setting bearer token to Global level.**
ajaxAdapter.defaultSettings = {
headers: ({ "Authorization": "Bearer " + token })
};
myAPICall(args.email).then({}).fail({});
});
}
});
Server code which is validating RS256 signed JWT with OWIN.
private void ConfigureAuthZero(IAppBuilder app)
{
var issuer = $"https://{ConfigurationManager.AppSettings["Auth0:Domain"]}/";
var audience = ConfigurationManager.AppSettings["Auth0:ClientID"];
var apiIdentifier = ConfigurationManager.AppSettings["Auth0:ApiIdentifier"];
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
string certificatePath = HostingEnvironment.MapPath("~/mycertificate.cer");
var certificate = new X509Certificate2(certificatePath);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = audience,
ValidIssuer = issuer,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) => new X509SecurityKey(certificate)
}
});
}
My Problem:
The above server code won't authorize the user.
But if I set ValidAudience = "https://xxx.auth0.com/api/v2/" i.e to Auth0 API Identifier, then the API method successfully authorizes (status 200) the user.
But this time it won't give ClaimsIdentity.Claims with ClaimTypes.Email
What am I missing here?
My mistakes:
I should pass ApiIdentifier to ValidAudience value.
As I was passing accessToken while authorizing the user, by the
time the accessToken claims doesn't contain the ClaimTypes.Email, so
I need to set the rules in Auth0 as:How to set the rules in Auth0.
Which later I can check in my server api logic as(below code) to
validate the user.
(User.Identity as ClaimsIdentity)?.Claims.FirstOrDefault(c => c.Type == "you-have-set-this-rule-in-auth0")?.Value;
Just to Add-on, The Link worth reading while implementing the Auth0.
Auth0 has provided a nice nuget package Auth0.OpenIdConnectSigningKeyResolver which has a nice use in the above provided link.

Session Times out after 20 mins using Identity Server Hybrid Flow with Cookie Authentication

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

"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.