How to create JWT token using IdentityServer4 - identityserver4

In my application (.Net core application) using IdentityServer4, at present creates "Reference" Token for authentication. But I would need to change the token type from "Reference" type to "JWT" token. I found couple of articles regarding that and tried as mentioned, but still I am not able to get the "JWT" token and I am getting "Reference" token only.
I followed the details mentioned in the below sites, but no luck.
IdentityServer4 requesting a JWT / Access Bearer Token using the password grant in asp.net core
https://codebrains.io/how-to-add-jwt-authentication-to-asp-net-core-api-with-identityserver-4-part-1/
https://andrewlock.net/a-look-behind-the-jwt-bearer-authentication-middleware-in-asp-net-core/
Can anyone let me know how could we change the token type from "Reference" to "JWT" token? Is there any custom code/class to be created to achieve this?
Below is the code used in my Client class.
new Client
{
ClientId = "Client1",
ClientName = "Client1",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = new List<string>
{
IdentityScope.OpenId,
IdentityScope.Profile,
ResourceScope.Customer,
ResourceScope.Info,
ResourceScope.Product,
ResourceScope.Security,
ResourceScope.Sales,
ResourceScope.Media,
ResourceScope.Nfc,
"api1"
},
AllowOfflineAccess = true,
AlwaysSendClientClaims = true,
UpdateAccessTokenClaimsOnRefresh = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowAccessTokensViaBrowser = true,
// Use reference token so mobile user (resource owner) can revoke token when log out.
// Jwt token is self contained and cannot be revoked
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = CommonSettings.AccessTokenLifetime,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
RefreshTokenExpiration = TokenExpiration.Sliding,
AbsoluteRefreshTokenLifetime = CommonSettings.AbsoluteRefreshTokenLifetime,
SlidingRefreshTokenLifetime = CommonSettings.SlidingRefreshTokenLifetime,
IncludeJwtId = true,
Enabled = true
},
And in my startup.cs, I have this below code.
public void ConfigureServices(IServiceCollection services)
{
var connStr = ConfigurationManager.ConnectionStrings[CommonSettings.IDSRV_CONNECTION_STRING].ConnectionString;
services.AddMvc();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
// base-address of your identityserver
options.Authority = "http://localhost:1839/";
// name of the API resource
options.Audience = "api1";
options.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
}
);
var builder = services.AddIdentityServer(options => setupAction(options))
.AddSigningCredential(loadCert())
.AddInMemoryClients(Helpers.Clients.Get())
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
.AddInMemoryApiResources(Resources.GetApiResources()).AddDeveloperSigningCredential()
.AddConfigStoreCache().AddJwtBearerClientAuthentication()
//Adds a key for validating tokens. They will be used by the internal token validator and will show up in the discovery document.
.AddValidationKey(loadCert());
builder.AddConfigStore(options =>
{
//CurrentEnvironment.IsEnvironment("Testing") ?
// this adds the config data from DB (clients, resources)
options.ConfigureDbContext = dbBuilder => { dbBuilder.UseSqlServer(connStr); };
})
.AddOperationalDataStore(options =>
{
// this adds the operational data from DB (codes, tokens, consents)
options.ConfigureDbContext = dbBuilder => { dbBuilder.UseSqlServer(connStr); };
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = CommonSettings.TokenCleanupInterval;
});
}
Kindly let me know, what change(s) to be done to get JWT token. Thanks in advance.

Related

How to validate Identityserver4 token validation in webapi?

Have you tried to ID4 connect with web api (.net framework 4.6) I follow below the tutorial but APIResource with secret key is not working. it also not giving any error if I give wrong API resource name and secret.
https://nahidfa.com/posts/identityserver4-and-asp-.net-web-api/
Source code
var IDSBearerOption = new IdentityServerBearerTokenAuthenticationOptions
{
AuthenticationType = "Bearer",
Authority = "https://localhost:5000",
ValidationMode = ValidationMode.Local,
RequiredScopes = new[] { "api1" },
PreserveAccessToken = true,
RoleClaimType = "role",
ValidAudiences = new[] { "TestAPI1" } ,
ClientId = "TestAPI1", //api resource name
ClientSecret = "secret1" //api resource secret
};
app.UseIdentityServerBearerTokenAuthentication(IDSBearerOption);
Is it possible to validate the token in webapi .net framework4.6?
You'll have to share cookies and set your bearer token auth options inside OWIN in startup:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieHttpOnly = false,
CookieSecure = CookieSecureOption.Always,
CookieName = "MySharedCookieName"
});
app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = authorityUrl,
RequiredScopes = new[] {"MyAPIName"},
ClientId = "MyAPIName",
ClientSecret = "secret",
AuthenticationType = "Bearer",
NameClaimType = "name",
RoleClaimType = "role",
SigningCertificate = x509Cert
});
Just make sure your cookie names match in IDS4 and all other apps

IdentityServer4 External Authentication (Azure Active Directory) fails

I created project from template: "is4aspid" and added Azure Active Directory authentication.
In Startup.cs added this code:
// preserve OIDC state in cache (solves problems with AAD and URL lenghts)
services.AddOidcStateDataFormatterCache();
// cookie policy to deal with temporary browser incompatibilities
services.AddSameSiteCookiePolicy();
services.AddAuthentication()
.AddOpenIdConnect("aad", "Sign-in with Azure AD", options =>
{
options.Authority = "https://login.microsoftonline.com/common";
options.ClientId = "myClientId";
options.ClientSecret = "myClientSecret";
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.SignOutScheme = IdentityServerConstants.SignoutScheme;
options.ResponseType = "id_token";
options.CallbackPath = "/signin-aad";
options.SignedOutCallbackPath = "/signout-callback-aad";
options.RemoteSignOutPath = "/signout-aad";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
NameClaimType = "name",
RoleClaimType = "role"
};
});
I added redirect uri on Azure: http://localhost:5000/signin-aad
Error occure where Callback method is called.
HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme) Success is always false
[HttpGet]
public async Task<IActionResult> Callback()
{
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
if (result?.Succeeded != true)
{
throw new Exception("External authentication error");
}
// some code
}
Where is my error?
Solution
Must define IdentityConstants.ExternalScheme in startup and callback

Getting a Refresh Token from IdentitySever4

I have a Blazor web app that connects to a different Identity Server 4 server. I can get the login to work correctly and pass the access token back the Blazor. However, when the token expires I don't know how to go out and get a new access token? Should I be getting a refresh token and then an access token? I am confused on how this all works.
Blazor Code
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = AzureADDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(AzureADDefaults.AuthenticationScheme, options =>
{
options.Authority = "https://localhost:44382";
options.RequireHttpsMetadata = true;
options.ClientId = "client";
options.ClientSecret = "secret";
options.ResponseType = "code id_token token";
options.SaveTokens = true;
options.Scope.Add("IdentityServerApi");
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("roles");
options.Scope.Add("offline_access");
});
IdentityServer4 Setup
...
new Client
{
ClientId = "client",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = true,
RequireClientSecret = true,
RequireConsent = false,
RedirectUris = { "https://localhost:44370/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:44370/signout-callback-oidc" },
AllowedScopes = { "openid", "profile", "email", "roles", "offline_access",
IdentityServerConstants.LocalApi.ScopeName
},
AllowedCorsOrigins = { "https://localhost:44370" },
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowOfflineAccess = true,
AccessTokenLifetime = 1,//testing
UpdateAccessTokenClaimsOnRefresh = true
},
...
UPDATE:
I have updated my code to offline_access for the client and server (thanks for the update below). My next question is how do I inject the request for the refresh token in Blazor once I get rejected because the access token is expired?
I have the Blazor app making calls back to the API (which validates the access token).
public class APIClient : IAPIClient
{
private readonly HttpClient _httpClient;
//add the bearer token to the APIClient when the client is used
public APIClient(IHttpContextAccessor httpAccessor, HttpClient client, IConfiguration configuration)
{
var accessToken = httpAccessor.HttpContext.GetTokenAsync("access_token").Result;
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestVersion = new Version(2, 0);
client.BaseAddress = new Uri(configuration["Api_Location"]);
_httpClient = client;
_logger = logger;
}
What do I need to add to my API calls to validate?
Yes, you should obtain a refresh token as well to keep getting new access tokens. To get a refresh token from IdentityServer you need to add the 'offline_access' scope in the 'AllowedScopes' property of your client. You also need to set the 'AllowOfflineAccess' property on your client to true.
After that you need to include 'offline_access' to the scopes sent by the client and you should receive a refresh token in the response.
To use the refresh token, send a request to the token endpoint with everything you sent for the code exchange except replace the 'code' param with 'refresh_token' and change the value for 'grant_type' from 'code' to 'refresh_token'. The IdentityServer4 response to this request should contain an id_token, an access_token, and a new refresh_token.
I think I have found an answer (given the push from Randy). I did something familiar to this post, where I created a generic method in my APIClient.
public async Task<T> SendAsync<T>(HttpRequestMessage requestMessage)
{
var response = await _httpClient.SendAsync(requestMessage);
//test for 403 and actual bearer token in initial request
if (response.StatusCode == HttpStatusCode.Unauthorized &&
requestMessage.Headers.Where(c => c.Key == "Authorization")
.Select(c => c.Value)
.Any(c => c.Any(p => p.StartsWith("Bearer"))))
{
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "refresh_token"),
new KeyValuePair<string, string>("refresh_token", _httpAccessor.HttpContext.GetTokenAsync("refresh_token").Result),
new KeyValuePair<string, string>("client_id", "someclient"),
new KeyValuePair<string, string>("client_secret", "*****")
};
//retry do to token request
using (var refreshResponse = await _httpClient.SendAsync(
new HttpRequestMessage(HttpMethod.Post, new Uri(_authLocation + "connect/token"))
{
Content = new FormUrlEncodedContent(pairs)})
)
{
var rawResponse = await refreshResponse.Content.ReadAsStringAsync();
var x = Newtonsoft.Json.JsonConvert.DeserializeObject<Data.Models.Token>(rawResponse);
var info = await _httpAccessor.HttpContext.AuthenticateAsync("Cookies");
info.Properties.UpdateTokenValue("refresh_token", x.Refresh_Token);
info.Properties.UpdateTokenValue("access_token", x.Access_Token);
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", x.Access_Token);
//retry actual request with new tokens
response = await _httpClient.SendAsync(new HttpRequestMessage(requestMessage.Method, requestMessage.RequestUri));
}
}
if (typeof(T).Equals(typeof(HttpResponseMessage)))
return (T)Convert.ChangeType(response, typeof(T));
else
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
}
I don't like that I have to call AuthenticateAsync. Yet, that seems to be the way I have found to get access to the UpdateTokenValue method to delete and then re-add the new access token.

Access token not getting saved in database after integrating with Identity Server EF

I am implementing EF for Identity framework 4 so that i can store tokens in database. My question is, does it store code, Access token and Refresh token all in database (http://docs.identityserver.io/en/latest/reference/ef.html).
After i implemented code from the document link, that i pasted above, i am getting refresh token data in database but not access token. Also when i try to get a new access token from refresh token, i get new refresh token as well for which i dont see a new entry in database (PersistedGrants) table.
StartUp:
services.AddIdentityServer()
.AddDeveloperSigningCredential(filename: "key.rsa")
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString);
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30; // interval in seconds
})
.AddConfigurationStoreCache()
Client:
ClientId = "testclient",
ClientName = "testclient",
ClientSecrets = { "password" },
AllowedGrantTypes = GrantTypes.Implicit,
RequireConsent = false,
RedirectUris = { "https://testapp.azurewebsites.net/signin-oidc"},
PostLogoutRedirectUris = { "https://testapp.azurewebsites.net/signout-callback-oidc" },
FrontChannelLogoutUri = "https://testapp.azurewebsites.net/FrontChannelLogout",
//FrontChannelLogoutUri = "https://testapp.azurewebsites.net/signout-callback-oidc",
AllowedScopes = new List<string>
{
"OpenId",
"Profile",
},
AllowOfflineAccess = true

Ca not get Claims information while login Azure active B2B directory

I can not get ClaimsPrincipal after login in azure Ad Web API,
Below is my code added in startup.auth.cs
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Scope= OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = context =>
{
context.HandleResponse();
return Task.FromResult(0);
}
},
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},
});
I get Access Token in
result = await authContext.AcquireTokenAsync(todoListResourceId, clientCredential);
but can not get ClaimsPrincipal. I get AuthenticationType = null, IsAuthenticated = null, Name = null.
My application use adal.js for UI side to get user information, and get user information successfully.
I got Solution for this problem.replace startup.auth.cs code with
app.UseWindowsAzureActiveDirectoryBearerAuthentication( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { TokenValidationParameters = new TokenValidationParameters { SaveSigninToken = true, ValidAudience = ConfigurationManager.AppSettings["ida:ClientId"], AuthenticationType = "Bearer" }, Tenant = ConfigurationManager.AppSettings["ida:Tenant"], });
this code and it working fine

Resources