IdentityServer4 RequestedClaimTypes is Empty - asp.net-core

In my profile service why is RequestedClaimTypes Empty? I am expecting the profile claims to be requested. And per this they should contain FamilyName and Given Name claim types.
GetIdentityResources
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
Client
new Client
{
ClientId = "46a0ab4a-1321-4d77-abe5-98f09310df0b",
ClientName = "TypeScript SPA client",
RequireClientSecret = false, // if false this is a public client.
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:3000/callback" },
PostLogoutRedirectUris = { "http://localhost:3000/" },
AllowedCorsOrigins = { "http://localhost:3000" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
},
RequireConsent = false,
},
oidc-client configuration typescript
const myOidcClientSettings: OidcClientSettings = {
authority: `${protocol}//${hostname}:5000`,
client_id: '46a0ab4a-1321-4d77-abe5-98f09310df0b',
post_logout_redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/`,
redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/callback`,
response_type: 'id_token token',
scope: 'openid profile'
};
const myUserManagerSettings: UserManagerSettings = {
...myOidcClientSettings,
automaticSilentRenew: false,
filterProtocolClaims: true,
loadUserInfo: true,
monitorSession: false,
silent_redirect_uri: `${protocol}//${hostname}${port ? `:${port}` : ''}/callback`,
};
Inside the Login Post I add the following claims:
Claim[] claims =
{
new Claim(JwtClaimTypes.Name, $"{loginResponse.FirstName} {loginResponse.LastName}"),
new Claim(JwtClaimTypes.Email, loginResponse.EmailAddress),
new Claim(JwtClaimTypes.PhoneNumber, loginResponse.PhoneNumber),
new Claim(JwtClaimTypes.FamilyName, loginResponse.LastName),
new Claim(JwtClaimTypes.GivenName, loginResponse.FirstName),
//new Claim(JwtClaimTypes.AuthorizationCodeHash, aRequest.Password), // The Password will be need by the BFF but can NOT be sent to the Typescript client
};
await HttpContext.Authentication.SignInAsync(subjectId, userName, authenticationProperties, claims);
ProfileService
public Task GetProfileDataAsync(ProfileDataRequestContext aProfileDataRequestContext)
{
Logger.LogDebug("Get profile called for {subject} from {client} with {claimTypes} because {caller}",
aProfileDataRequestContext.Subject.GetSubjectId(),
aProfileDataRequestContext.Client.ClientName,
aProfileDataRequestContext.RequestedClaimTypes,
aProfileDataRequestContext.Caller);
if (aProfileDataRequestContext.RequestedClaimTypes.Any())
{
aProfileDataRequestContext.AddFilteredClaims(aProfileDataRequestContext.Subject.Claims);
}
return Task.FromResult(0);
}
Resulting User Info that profile does NOT contain the profile items: (Shortend for readability
"User info": {
"id_token": "eyJhbGciOiJSUzI1N",
"session_state": "M5uV9nYzvmlWjvpjmX--OOPcwAEeVesV7aG9ZO0svS8.8f757e9a033183149734adb156fbb39d",
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6",
"token_type": "Bearer",
"scope": "openid profile",
"profile": {
"sid": "4372a4cbb9938449a39d72db1a9fc6f0",
"sub": "TestDemo12#gmail.com",
"auth_time": 1505037917,
"idp": "local",
"amr": [
"pwd"
]
},
"expires_at": 1505042091,
"state": {
"returnUrl": "/en-us/test"
}
}

It looks like you have to include the following option to your client,
AlwaysIncludeUserClaimsInIdToken = true
So that your client will include the claims in the token.

Related

Single sign out in identity server 4

###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);
}

Always getting "invalid_client" from Identity Server

Everything seems normal but it is not working, It returns "Invalid_Client" - (400 - Bad request).
Both side so simple below;
Identity Server Code:
new Client
{
ClientId = "js",
ClientSecrets = {
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.ClientCredentials,
RequireClientSecret = false,
AllowedScopes =
{
"api1"
}
}
Javascript Client Code:
axios.post('http://localhost:5000/connect/token',request, {
headers: {
'client_id' : 'js',
'client_secret' : 'secret',
'grant_type': 'client_credentials',
'scope' : 'api1'
}});
The parameters should be passed in request body not request header , you can modify the client script as :
const params = new URLSearchParams();
params.append('client_id', 'js');
params.append('client_secret', 'secret');
params.append('grant_type', 'client_credentials');
params.append('scope', 'api1');
axios.post('http://localhost:5000/connect/token', params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}).then(function (response) {
console.log(response.data);
});
And also configure CORS is to use the AllowedCorsOrigins collection on the client configuration:
new Client
{
ClientId = "js",
ClientSecrets = {
new Secret("secret".Sha256())
},
AllowedCorsOrigins= new List<string>() { "http://localhost:5002" },
AllowedGrantTypes = GrantTypes.ClientCredentials,
RequireClientSecret = false,
AllowedScopes =
{
"api1"
}
},
Should not use normall fetch or axis.
Because the service is Oauth service and should be use 'client-oauth2' library.
Code Example:
var ClientOAuth2 = require("client-oauth2");
var authRequest = new ClientOAuth2({
clientId: IDENTITY_CONFIG.client_id,
clientSecret: IDENTITY_CONFIG.client_secret,
accessTokenUri: IDENTITY_CONFIG.token_endpoint,
scopes: [IDENTITY_CONFIG.grantType]
});
return authRequest.credentials.getToken();

How to use Expo AppAuth module with IdentityServer4

I am trying to use the Expo AppAuth module to do authentication using IdentityServer4 in react native. Cant seem to get the redirectUri settings right. I'm getting an 'invalid redirect uri" error when i redirect to identityServer.
This is my client on identityserver
return new List<Client>
{
new Client
{
ClientName = "client",
ClientId = "client",
RequirePkce = true,
AllowedGrantTypes = GrantTypes.Code,
RequireClientSecret = false,
RequireConsent = true,
RedirectUris =
{
"host.exp.Exponent" //Is this correct
},
AllowOfflineAccess = true,
RefreshTokenUsage = TokenUsage.ReUse,
AllowedScopes = { "openid", "profile"},
}
};
My config settings for AppAuth are
const config = {
issuer: 'http://localhost:3000',
clientId: 'client',
scopes: ['profile', 'openid'],
redirectUri: "host.exp.Exponent"
}
You should specify redirectUri as the address value.
AppAuth Definitions:
async function _executeAsync(props: OAuthProps): Promise<TokenResponse> {
if (!props.redirectUrl) {
props.redirectUrl = getDefaultOAuthRedirect();
}
assertValidProps(props);
return await ExpoAppAuth.executeAsync(props);
}
export function getDefaultOAuthRedirect(): string {
return `${ExpoAppAuth.OAuthRedirect}:/oauthredirect`;
}

Identityserver4 redirection is not working after log in

Version
Identiotyserver4 2.2.0
ASP.NETCore 2.0.5
1. Client-Authcode
2.new Client {
3. ClientId = "authorizationCodeClient2",
4. ClientName = "Authorization Code Client",
5.ClientSecrets = new List { new Secret { Value = "mysecret".Sha512() } },
Enabled = true,
6.AllowedGrantTypes = new List { "authorization_code" }, //DELTA //IdentityServer3 wanted Flow = Flows.AuthorizationCode,
7. RequireConsent = true,
8. AllowRememberConsent = false,
9. RedirectUris =
new List {
"http://localhost:5436/account/oAuth2/",
},
10. PostLogoutRedirectUris =
new List {"http://localhost:5436/",},
AllowedScopes = new List {
"api1"
},
AccessTokenType = AccessTokenType.Jwt
}
2. Client -Implicit
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
ClientUri = "https://localhost:5003",
Properties = new Dictionary<string, string>
{
{ "StartLoginFragment", "/callback.html" }
},
RedirectUris = { "http://localhost:5003/callback.html" },
PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
AllowedCorsOrigins = { "http://localhost:5003" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
We are able to login with credentials but after redirection is not happening. It's returning to server login page

Configuration for getting JWT from IdentityServer with Aurelia-Auth

I am having trouble getting an JWT from IdentityServer3. I am using aurelia with aurelia-auth.
The error I am getting from IdentityServer is
"The client application is not known or is not authorized."
Was wondering what I am missing in the configuration? Configuration is below
//Server Clients
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new List<Client> {
new Client {
ClientName = "AureliaApplication",
Enabled = true,
ClientId = "aureliaClient",
AllowAccessToAllScopes = true,
Flow = Flows.ResourceOwner,
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600
}
};
}
}
//Aurelia-Auth Provider Config
var config = {
providers: {
IdentityServerV3: {
name:'IdentityServerV3',
url: '/auth/IdentityServerV3',
authorizationEndpoint: 'https://localhost:44300/core/connect/authorize',
redirectUri: window.location.origin || window.location.protocol + '//' + window.location.host,
scope: ['openid'],
scopePrefix: 'openid',
scopeDelimiter: '&',
display: 'popup',
type: '2.0',
clientId: 'aureliaClient',
popupOptions: { width: 1020, height: 618 }
}
}
}
export default config;
You need to configure the scope of the client in the IdentityServer
new Client
{
ClientId = "Aurelia Client",
ClientName = "aureliaClient",
ClientSecrets = new List<Secret> {
new Secret(Constants.IdentitySecret.Sha256())
},
Flow = Flows.Hybrid,
RequireConsent = true,
AllowRememberConsent = true,
RedirectUris = new List<string> {
"http://localhost:9000"
},
PostLogoutRedirectUris = new List<string> {
"http://localhost:9000"
},
AllowedScopes = new List<string> {
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Roles,
"apiAccess"
}
}
The Aurelia config has to have to correct url to the different IdentityServer endpoint. These endpoints can usually be found in the openid-configuration of the server (in this example it would be : https://localhost:44301/core/.well-known/openid-configuration).
The same scope as define in the client configuration in the IdentityServer
var config = {
baseUrl : 'https://localhost:44301/core',
tokenName : 'id_token',
profileUrl: '/connect/userinfo',
unlinkUrl : '/connect/endsession',
logoutRedirect: '/',
loginRedirect : '#/',
providers : {
identSrv : {
name: 'identSrv',
url: '/connect/token',
authorizationEndpoint: 'https://localhost:44301/core/connect/authorize/',
redirectUri: window.location.origin || window.location.protocol + '//' + window.location.host,
scope: ['profile', 'apiAccess','openid', 'roles'],
responseType :'code id_token token',
scopePrefix: '',
scopeDelimiter: ' ',
requiredUrlParams: ['scope', 'nonce'],
optionalUrlParams: ['display'],
state: 'session_state',
display: 'popup',
type: '2.0',
clientId: 'jsClient',
flow: 'hybrid',
nonce : function(){
var val = ((Date.now() + Math.random()) * Math.random()).toString().replace(".", "");
return encodeURIComponent(val);
},
popupOptions: { width: 452, height: 633 }
}
}
Scott actually found the solution (I'm just using it to answer) and you can find an example on his github https://github.com/devscott/identityServer3Example