Single sign out in identity server 4 - authentication

###I am using identity server 4 for authentication for .net and angular apps.
if I log out from one client it does not log out from others.###
how can I delete the user session and implement single-signout for all clients
#the config class in identity server#
//MVC Client
new Client
{
ClientName = ".NET 4 MVC website",
ClientId = "net4mvcclient",
ClientSecrets =
{
new Secret("secret3".Sha256())
},
//Grant types are a way to specify how a client wants to interact with IdentityServer
AllowedGrantTypes = GrantTypes.Implicit,
RequireConsent = false,
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true,
RedirectUris = { $"{_config.GetValue<string>("IdentityServerSettings:MVCBaseUri")}/signin-oidc" },
PostLogoutRedirectUris = { $"{_config.GetValue<string>("IdentityServerSettings:MVCBaseUri")}/" },
AllowedScopes = {"openid", "profile", "offline_access", "api1", "api2" } ,
AllowedCorsOrigins = {$"{_config.GetValue<string>("IdentityServerSettings:MVCBaseUri")}"},
AccessTokenLifetime = 50000
},
//angular_spa
new Client {
RequireConsent = false,
ClientId = "angular_spa",
ClientName = "Angular SPA",
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes = { "openid", "profile", "offline_access", "api1", "api2" },
RedirectUris = { $"{_config.GetValue<string>("IdentityServerSettings:AngularBaseUri")}/#auth-callback/#" },
PostLogoutRedirectUris = { $"{_config.GetValue<string>("IdentityServerSettings:AngularBaseUri")}/" },
AllowedCorsOrigins = { $"{_config.GetValue<string>("IdentityServerSettings:AngularBaseUri")}" },
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 3600
}
};
startup class in MVC client
public void ConfigureIdentityServer(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
Authority = ConfigurationManager.AppSettings["IdentityServerUrl"], //Identity server Url
ClientId = "net4mvcclient",
ClientSecret = "secret3",
RedirectUri = ConfigurationManager.AppSettings["HourlyMVCUrl"] +"/signin-oidc", //Net4MvcClient's URL
PostLogoutRedirectUri = ConfigurationManager.AppSettings["HourlyMVCUrl"]+"/", //MVC Client URL
ResponseType = "id_token token",
RequireHttpsMetadata = false,
Scope = "openid profile api1 api2 offline_access",
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name"
},
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
return Task.FromResult(0);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var id_token_claim = n.OwinContext.Authentication.User.Claims.FirstOrDefault(x => x.Type == "id_token");
if (id_token_claim != null)
{
n.ProtocolMessage.IdTokenHint = id_token_claim.Value;
}
}
return Task.FromResult(0);
}
}
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
app.UseNLog((eventType) => LogLevel.Debug);
}
#the logout function in identity server#
public async Task<IActionResult> Logout(LogoutInputModel model)
{
// build a model so the logged out page knows what to display
var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
// Request.GetOwinContext().Authentication.SignOut();
await HttpContext.SignOutAsync();
await _signInManager.SignOutAsync();
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (vm.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}
if (vm.PostLogoutRedirectUri != null)
{
return SignOut(new AuthenticationProperties { RedirectUri = vm.PostLogoutRedirectUri }, vm.ExternalAuthenticationScheme);
}
return View("LoggedOut", vm);
}

Related

Unauthorised client when requesting for token using IdentityServer4 .NET Core 3.1

I'm trying to get IdentityServer4 to work but unfortunately no luck. I'll explain the issue in more detail. I'm using IdentityServer4 and also .NET core Identity. I have a .net core mvc application which has login page. You basically login with username and password. When you login I need to generate jwt token I'm doing this using the following code:
[HttpGet]
public async Task<IActionResult> GetClientToken(string clientId, string clientSecret, string grantType, string scope, string username, string password)
{
var serverClient = HttpClientFactory.CreateClient();
var discoveryDocument = await serverClient.GetDiscoveryDocumentAsync($"{Request.Scheme}://{Request.Host.Value}");
var tokenClient = HttpClientFactory.CreateClient();
var tokenResponse = await tokenClient.RequestPasswordTokenAsync(
new PasswordTokenRequest
{
ClientId = clientId,
ClientSecret = clientSecret,
GrantType = grantType,
Address = discoveryDocument.TokenEndpoint,
UserName = username,
Password = password,
Scope = scope,
});
if (!tokenResponse.IsError)
{
return Ok(new TokenResponseModel()
{
access_token = tokenResponse.AccessToken,
refresh_token = tokenResponse.RefreshToken,
expires_in = tokenResponse.ExpiresIn,
scope = tokenResponse.Scope,
token_type = tokenResponse.TokenType,
});
}
return BadRequest(tokenResponse.Error);
}
Every time I request for a token I get unauthorised client.
My seeding data is as follows:
public static IEnumerable<ApiResource> GetApis() =>
new List<ApiResource>
{
new ApiResource("AppointmentBookingApi"),
new ApiResource("PaymentApi", new string[] { "patient.portal.api.payment" }),
};
public static IEnumerable<IdentityResource> GetIdentityResources() =>
new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResource
{
Name = "patient.portal.api",
UserClaims =
{
"patient.portal",
},
}
};
public static IEnumerable<Client> GetClients() =>
new List<Client>
{
new Client
{
ClientId = "patient.portal.client.refresh",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
RequirePkce = true,
RedirectUris = { "https://localhost:44307/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:44307/Home/Index" },
AllowedScopes =
{
"AppointmentBookingApi",
"PaymentApi",
IdentityServerConstants.StandardScopes.OpenId,
"patient.portal.api",
},
AllowOfflineAccess = true,
RequireConsent = false,
},
new Client
{
ClientId = "patient.portal.client.code",
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes =
{
"AppointmentBookingApi",
},
},
};
does anyone know where I'm I going wrong here????

404 error after Apple authentication using OpenId Connect

adopting from scottbrady91.com, I'm trying to have an Apple external authentication on our site. I've had Microsoft one working, but not the Apple one yet. The user is already directed to appleid.apple.com, but after authentication, it's returned to https://iluvrun.com/signin-apple (which is correct), but this isn't handled and so the user gets a 404 error.
To be honest I don't know how signin-facebook, signin-google or signin-oidc work, but they just do. So I have problems figuring out why signin-apple isn't being handled.
The site is built using ASP.NET Web Forms. Below is what I have at Startup.Auth.cs:
namespace ILR
{
public partial class Startup {
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions("Apple")
{
ClientId = "com.iluvrun.login",
Authority = "https://appleid.apple.com/auth/authorize",
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
RedirectUri = "https://iluvrun.com/signin-apple",
PostLogoutRedirectUri = "https://iluvrun.com",
Scope = "name email",
ResponseType = OpenIdConnectResponseType.Code,
ResponseMode = OpenIdConnectResponseMode.FormPost,
CallbackPath = PathString.FromUriComponent("/signin-apple"),
Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = "https://appleid.apple.com/auth/authorize",
TokenEndpoint = "https://appleid.apple.com/auth/token"
},
TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://appleid.apple.com",
IssuerSigningKey = new JsonWebKeySet(GetKeysAsync().Result).Keys[0]
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
context.TokenEndpointRequest.ClientSecret = TokenGenerator.CreateNewToken();
return Task.CompletedTask;
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Account/Login?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
},
ProtocolValidator = new OpenIdConnectProtocolValidator
{
RequireNonce = false,
RequireStateValidation = false
}
}
);
}
private static async Task<string> GetKeysAsync()
{
string jwks = await new HttpClient().GetStringAsync("https://appleid.apple.com/auth/keys");
return jwks;
}
}
public static class TokenGenerator
{
public static string CreateNewToken()
{
const string iss = "CHM57Z5A6";
const string aud = "https://appleid.apple.com";
const string sub = "com.iluvrun.login";
const string privateKey = "XXXX"; // contents of .p8 file
CngKey cngKey = CngKey.Import(Convert.FromBase64String(privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
JwtSecurityToken token = handler.CreateJwtSecurityToken(
issuer: iss,
audience: aud,
subject: new ClaimsIdentity(new List<Claim> { new Claim("sub", sub) }),
expires: DateTime.UtcNow.AddMinutes(5),
issuedAt: DateTime.UtcNow,
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(new ECDsaSecurityKey(new ECDsaCng(cngKey)), SecurityAlgorithms.EcdsaSha256));
return handler.WriteToken(token);
}
}
}
Does anyone have any clue what I miss to get this working?
You are on the right track and your question helped me to quick start my own solution for Apple ID OpenIdConnect OWIN integration in my project. After finding your post here it took me quite long to fix all issues.
After using your code I've got the same 404 error.
404 error
This was due to unhandled exception in CreateNewToken() method, which wasn't able to generate valid token. In my case it was missing Azure configuration for my AppService more described in CngKey.Import on azure:
WEBSITE_LOAD_USER_PROFILE = 1
After setting this configuration in Azure I moved to next issue:
Token endpoint wasn't called
This was due to missing configuration in OpenIdConnectAuthenticationOptions:
RedeemCode = true
This option trigger all the next processing of the authentication pipeline inside OpenIdConnect (TokenResponseReceived, SecurityTokenReceived, SecurityTokenValidated)
AccountController.ExternalLoginCallback token processing issue
This was tricky. Because all you get is after calling
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
is getting:
loginInfo == null
So after reading lot of issue articles on this topic like OWIN OpenID provider - GetExternalLoginInfo() returns null and tries, the only final workaround was to add https://github.com/Sustainsys/owin-cookie-saver to my Startup.cs, which fixed problem of missing token cookies. Its marked as legacy, but it was my only option to fix this.
So final OpenIdConnect options config for my working solution is:
var appleIdOptions = new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "https://appleid.apple.com",
ClientId = "[APPLE_CLIENT_ID_HERE]",
Authority = "https://appleid.apple.com/auth/authorize",
SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
RedirectUri = "https://www.europeanart.eu/signin-apple",
PostLogoutRedirectUri = "https://www.europeanart.eu",
Scope = OpenIdConnectScope.Email,
RedeemCode = true,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
ResponseMode = OpenIdConnectResponseMode.FormPost,
CallbackPath = PathString.FromUriComponent("/signin-apple"),
Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = "https://appleid.apple.com/auth/authorize",
TokenEndpoint = "https://appleid.apple.com/auth/token"
},
TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "https://appleid.apple.com",
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = new JsonWebKeySet(GetKeys()).Keys,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
var clientToken = JwtTokenGenerator.CreateNewToken();
logger.LogInfo("Apple: clientToken generated");
context.TokenEndpointRequest.ClientSecret = clientToken;
logger.LogInfo("Apple: TokenEndpointRequest ready");
return Task.FromResult(0);
},
TokenResponseReceived = (context) =>
{
logger.LogInfo("Apple: TokenResponseReceived");
return Task.FromResult(0);
},
SecurityTokenReceived = (context) =>
{
logger.LogInfo("Apple: SecurityTokenReceived");
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
string userID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
logger.LogInfo("Apple: SecurityTokenValidated with userID=" + userID);
return Task.FromResult(0);
},
RedirectToIdentityProvider = (context) =>
{
logger.LogInfo("Apple: RedirectToIdentityProvider");
if(context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
{
logger.LogInfo("Apple: RedirectToIdentityProvider -> Authenticate()");
}
else if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Token)
{
logger.LogInfo("Apple: RedirectToIdentityProvider -> Token()");
}
return Task.FromResult(0);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
logger.LogError("Apple Authentication Failed.", context.Exception);
context.Response.Redirect("/Account/Login?errormessage=" + context.Exception.Message);
return Task.FromResult(0);
}
},
ProtocolValidator = new OpenIdConnectProtocolValidator
{
RequireNonce = false,
RequireStateValidation = false
}
};

How to properly use claims with IdentityServer4?

I'm trying to understand how this works, so please bear with me.
Here is my config for identity server:
public static IEnumerable<ApiResource> GetApiResources(IConfiguration configuration)
{
return new []
{
new ApiResource
{
Name = "invoices.api",
ApiSecrets =
{
new Secret("invoices.api.secret".Sha256()),
},
Scopes =
{
new Scope("invoices.api.scope"),
},
UserClaims =
{
"custom_role",
}
}
};
}
public static IEnumerable<Client> GetClients(IConfiguration configuration)
{
return new []
{
new Client
{
ClientId = "invoices.ui",
RequireConsent = false,
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AccessTokenType = AccessTokenType.Reference,
AllowedCorsOrigins = configuration.GetSection("Redirect").Get<RedirectOptions>().AllowedCorsOrigins.ToList(),
RedirectUris = configuration.GetSection("Redirect").Get<RedirectOptions>().RedirectUris.ToList(),
PostLogoutRedirectUris = configuration.GetSection("Redirect").Get<RedirectOptions>().PostLogoutRedirectUris.ToList(),
ClientSecrets =
{
new Secret("invoices.ui.secret".Sha256())
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
"invoices.api.scope",
},
}
};
}
public static IEnumerable<TestUser> GetUsers(IConfiguration configuration)
{
return new []
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "123",
Claims =
{
new Claim("custom_role", "user"),
},
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "123",
Claims =
{
new Claim("custom_role", "admin"),
},
}
};
}
public static IEnumerable<IdentityResource> GetIdentityResources(IConfiguration configuration)
{
return new []
{
new IdentityResources.OpenId(),
};
}
And this is how my MVC client is setup:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie(opts =>
{
//opts.ExpireTimeSpan = TimeSpan.FromSeconds(60);
})
.AddOpenIdConnect("oidc", opts =>
{
opts.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opts.DisableTelemetry = true;
opts.Authority = Configuration.GetValue<string>("IdentityServer");
opts.RequireHttpsMetadata = false;
opts.ClientId = "invoices.ui";
opts.ClientSecret = "invoices.ui.secret";
opts.ResponseType = "code id_token";
opts.SaveTokens = true;
opts.GetClaimsFromUserInfoEndpoint = true;
opts.Scope.Clear();
opts.Scope.Add("openid");
opts.Scope.Add("invoices.api.scope");
});
After a user is authenticated, i'm trying to see it's claims in view like this:
#foreach (var claim in User.Claims)
{
<dt>#claim.Type</dt>
<dd>#claim.Value</dd>
}
But the list doesn't contain any "custom_role" claim.
The identity server logs shows that the user info has been requested by the client from user info endpoint, but my "custom_role" wasn't transfered there, however it shows in logs of identity server, that user has it.
How to access my custom claims in my MVC app?
I need to get them from user endpoint and use for authorization.
If you ask for an Access Token and Identity Token ("code id_token") Identity Server will not include user claims by default.
The solution is to set AlwaysIncludeUserClaimsInIdToken to true. See http://docs.identityserver.io/en/release/reference/client.html
The explanation on why this settings exists is here: https://leastprivilege.com/2016/12/14/optimizing-identity-tokens-for-size/
Seems that adding an identity resource with specified claims solves the problem even with built-in ProfileService implementation:
public static IEnumerable<IdentityResource> GetIdentityResources(IConfiguration configuration)
{
return new []
{
new IdentityResources.OpenId(),
new IdentityResource
{
Name = "roles.scope",
UserClaims =
{
"custom_role",
}
}
};
}
Also added it as a scope for a client:
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
"invoices.api.scope",
"roles.scope",
},

Error connecting to https://localhost:5002/.well-known/openid-configuration

I am using Identity Server 4 and ASP.NET Core 2.1.
I have an API and an Auth application and tried to following:
var discoveryClient = new DiscoveryClient("https://localhost:5002");
discoveryClient.Policy.RequireHttps = true;
var discovery = await discoveryClient.GetAsync();
if (discovery.IsError)
Console.WriteLine(discovery.Error);
But I got the following error:
Error connecting to https://localhost:5002/.well-known/openid-configuration:
The SSL connection could not be established
The remote certificate is invalid according to the validation procedure.
However if I access the url "https://localhost:5002/.well-known/openid-configuration" in the browser I get the correct information and not errors.
What am I doing wrong?
The API application has the following configuration:
public void ConfigureServices(IServiceCollection services) {
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddRouting(x => { x.LowercaseUrls = true; });
services.AddHsts(x => {
x.Preload = true;
x.IncludeSubDomains = true;
x.MaxAge = TimeSpan.FromDays(60);
});
services.AddHttpsRedirection(x => {
x.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
x.HttpsPort = 5001;
});
services.AddApiVersioning(x => {
x.ApiVersionSelector = new CurrentImplementationApiVersionSelector(x);
x.AssumeDefaultVersionWhenUnspecified = true;
x.DefaultApiVersion = new ApiVersion(1, 0);
x.ReportApiVersions = false;
});
services.AddCors(x => {
x.AddPolicy("AllowAll", y => y.AllowAnyMethod().AllowAnyOrigin().AllowAnyHeader());
});
services.AddAuthorization();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(x => {
x.ApiName = "api";
x.Authority = "https://localhost:5002";
x.RequireHttpsMetadata = false;
});
} // ConfigureServices
public void Configure(IApplicationBuilder application, IHostingEnvironment environment) {
if (environment.IsDevelopment()) {
application.UseDeveloperExceptionPage();
} else {
application.UseHsts();
}
application.UseAuthentication();
application.UseHttpsRedirection();
application.UseMvc();
} // Configure
And the Auth application with the following configuration:
public void ConfigureServices(IServiceCollection services) {
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddRouting(x => { x.LowercaseUrls = true; });
services.AddHsts(x => {
x.Preload = true;
x.IncludeSubDomains = true;
x.MaxAge = TimeSpan.FromDays(60);
});
services.AddHttpsRedirection(x => {
x.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
x.HttpsPort = 5002;
});
services
.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetTestUsers());
} // ConfigureServices
public void Configure(IApplicationBuilder application, IHostingEnvironment environment) {
if (environment.IsDevelopment()) {
application.UseDeveloperExceptionPage();
} else {
application.UseHsts();
}
application.UseHttpsRedirection();
application.UseIdentityServer();
application.UseMvc();
} // Configure
Where Config that defines Clients, Resources and Test Users is:
public class Config {
public static List<ApiResource> GetApiResources() {
return new List<ApiResource> {
new ApiResource("api", "API Resource")
};
}
public static List<IdentityResource> GetIdentityResources() {
return new List<IdentityResource> {
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
}
public static List<Client> GetClients() {
return new List<Client> {
new Client {
ClientId = "app",
ClientName = "APP Client",
ClientSecrets = { new Secret("app".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = { "api" }
},
new Client {
ClientId = "mvc",
ClientName = "MVC Client",
ClientSecrets = { new Secret("mvc".Sha256()) },
Enabled = true,
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AllowOfflineAccess = true,
RequireConsent = false,
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002" },
FrontChannelLogoutUri = "http://localhost:5002/signout-oidc",
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api"
}
},
new Client {
ClientId = "spa",
ClientName = "SPA Client",
ClientSecrets = { new Secret("spa".Sha256()) },
Enabled = true,
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"api"
},
RedirectUris = { "https://localhost:5000" },
PostLogoutRedirectUris = { "https://localhost:5000/home" },
AllowedCorsOrigins = { "https://localhost:5000" }
}
};
}
public static List<TestUser> GetTestUsers() {
return new List<TestUser> {
new TestUser { SubjectId = "1", Username = "john", Password = "john", Claims = new List<Claim> { new Claim("name", "John") } },
};
}
} // Config

The function IntrospectionClient in Identityserver4 always returns Unauthorized

I want to use the function IntrospectionClient to verify the token in identity server, but it always returns: Unauthorized
Identity server startup code:
services.AddIdentityServer()
.AddSigningCredential(myPrivatecert)
.AddInMemoryApiResources(Config.GetResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetTestUsers())
.AddValidationKey(myPubliccert)
.AddJwtBearerClientAuthentication()
.AddValidators();
I add a api resource, a client info and a test user in config file
My Config is :
public class Config
{
//ApiResource
public static IEnumerable<ApiResource> GetResources()
{
return new List<ApiResource>
{
new ApiResource("api")
};
}
//Client
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client()
{
ClientId = "site",
AccessTokenLifetime = 180,
RefreshTokenExpiration = TokenExpiration.Absolute,
AbsoluteRefreshTokenLifetime = 1800,
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AlwaysSendClientClaims = true,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
AllowedScopes =
{
"api"
},
AccessTokenType = AccessTokenType.Jwt,
AllowOfflineAccess = true
},
};
}
//TestUser
public static List<TestUser> GetTestUsers()
{
return new List<TestUser>{
new TestUser{
SubjectId="1",
Username="wyt",
Password="123456",
Claims = new List<Claim>()
{
new Claim("wawa", "aoao"),
}
}
};
}
}
My Client Startup is:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).
AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:6001";
options.RequireHttpsMetadata = false;
options.ApiName = "api";
});
get token :
//token
var tokenClient = new TokenClient(dico.TokenEndpoint, "site", "secret");
var tokenResponse = tokenClient.RequestResourceOwnerPasswordAsync("wyt", "123456").Result;
want use introspection:
var introspectionClient = new IntrospectionClient(
dico.IntrospectionEndpoint,
"api",
"secret");
var vresponse = await introspectionClient.SendAsync
(
new IntrospectionRequest { Token = tokenResult.AccessToken }
);
but the vresponse is always Unauthorized
where is wrong?
I ran into the same issue, the documentation is not very clear.
You need to add an ApiSecret to your ApiResource:
public static IEnumerable<ApiResource> GetResources()
{
return new List<ApiResource>
{
new ApiResource("api")
{
ApiSecrets = { new Secret("secret".Sha256()) }
}
};
}
And then you call the introspection endpoint passing the api name and the api secret, like you did:
var introspectionClient = new IntrospectionClient(
dico.IntrospectionEndpoint,
"api",
"secret");