Always getting "invalid_client" from Identity Server - asp.net-core

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();

Related

how to add authorization to gRpc headers from Vue.js

first of all . i created a gRpc service project from VS2019, and added Jwt Bearer Authentication and Authorization.
when client app (VS code + Vue.js) request login service method , a token will be sent.
string key = "AB6B4DC1-FABF-47AE-A585-4AD154758B05";
var securityKey = new SymmetricSecurityKey(Guid.Parse(key).ToByteArray());
var claims = new[] {
new Claim(ClaimTypes.Name, clientId),
new Claim(ClaimTypes.NameIdentifier,clientId)
};
var token = new JwtSecurityToken("client",
"client",
claims,
notBefore:DateTime.Now,
expires: DateTime.Now.AddSeconds(60),
signingCredentials: new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256)
);
return new JwtSecurityTokenHandler().WriteToken(token);
and then jwt configuration is
var key = "AB6B4DC1-FABF-47AE-A585-4AD154758B05";
options.TokenValidationParameters = new TokenValidationParameters
{
ClockSkew = TimeSpan.FromSeconds(30),
ValidateIssuer = true,
ValidateAudience = true,
AudienceValidator = (m, n, z) =>
{
return m != null && m.FirstOrDefault().Equals("");
},
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidAudience = "client",
ValidIssuer = "client",
IssuerSigningKey = new SymmetricSecurityKey(Guid.Parse(key).ToByteArray())
};
the problem is i can't pass authorization as the header item to gRpc Server
code bellow:
'
import { Metadata } from '#grpc/grpc-js/build/src/metadata'
import { CallCredentials } from '#grpc/grpc-js/build/src/call-credentials'
export default {
name: 'App',
created () {
this.client = new GreeterClient('https://localhost:5001', CallCredentials.createFromPlugin, null)
this.appClient = new AppClient('https://localhost:5001', null, null)
},
methods: {
loginSys () {
var lm = new LoginModel()
lm.setLoginname('admin')
lm.setLoginpwd('abc123!##')
lm.setLogincode('kkkkkrrr')
var appInfo = new AppInfo()
appInfo.setAppid('asdfasdfasdf')
appInfo.setApptoken('12345678901234567890')
appInfo.setModel(lm)
this.appClient.login(appInfo, {}, (err, response) => {
debugger
if (err) {
console.log(`Unexpected error for sayHello: code = ${err.code}, message = "${err.message}"`)
} else {
console.log(response.getMessage())
console.log(response.getIssucceed())
if (response.hasUserinfo()) {
var userInfo = response.getUserinfo()
console.log(userInfo.getAvatar())
console.log(userInfo.getUsername())
console.log(userInfo.getCellphone())
}
}
})
},
get1 () {
var token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiY2xpZW50SWQiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6ImNsaWVudElkIiwibmJmIjoxNjI5MDM0MzkxLCJleHAiOjE2MjkwMzQ0NTEsImlzcyI6IkFpMi5XZWIiLCJhdWQiOiJBaTIuV2ViIn0.1QpFsvzRvlBvTPNQ4hzETIDaLfmUsxmVvXaRyXVskjI'
var metadata = new Metadata()
metadata.add('authorization', 'Bearer ' + token)
var request = new HelloRequest()
request.setName('World')
this.client.sayHello(request, metadata, (err, response) => {
if (err) {
console.log(`Unexpected error for sayHello: code = ${err.code}, message = "${err.message}"`)
} else {
console.log(response.getMessage())
}
})
}
}
}
method : get1 is used to do the "authorization" test .
when i executed this method , i found authorization doesn't exist in http headers.
i don't know why , and what should i do ?
i got the answer.
const token = 'eyJhbGciOiJIUzyXVskjI'
const metadata = { 'authorization': 'Bearer ' + token }

IdentityServer4 redirect callback cors error

in my identityserver4 redirect callback i get cors error.
i enabled cors policy, and used identityserver4 defaultcors class with AllowAll to true.
and i have my domain name in AllowedCorsOrigins in client config.
i tried any way and all articles but it always give cors error.
but if i try to make ajax request in that callback page it dont give me cors error to same page.
i cant understand it.
i have http://ids.example.com
and i have http://example.com
i there anything i can do to solve it. and this configuration works perfect in localhost.
Ids Config:
services.AddSingleton<ICorsPolicyService>(container =>
{
var logger = container.GetRequiredService<ILogger<DefaultCorsPolicyService>>();
return new DefaultCorsPolicyService(logger)
{
AllowAll = true
};
});
Client Config:
new Client
{
ClientId = "js.admin.prod",
ClientName = "Instrumententafel",
RequireClientSecret = false,
RequireConsent = false,
AllowedGrantTypes = GrantTypes.Code,
AccessTokenType = AccessTokenType.Reference,
// AccessTokenLifetime = 299,
RequirePkce = true,
AllowPlainTextPkce = false,
AllowAccessTokensViaBrowser = true,
AllowOfflineAccess = true,
UpdateAccessTokenClaimsOnRefresh = true,
RedirectUris = new List<string>
{
"http://example.com",
"http://example.com/logincb.html",
"http://example.com/silent-renew.html"
},
PostLogoutRedirectUris = new List<string>
{
"http://example.com/",
"http://example.com"
},
AllowedCorsOrigins = new List<string>
{
"http://example.com"
},
AllowedScopes = new List<string>
{
"openid",
"profile",
"email",
"phone",
"role",
"api1.full"
}
}
logincb.html:
<script src="./oidc-client.js"></script>
<script crossorigin>
var mgr = new Oidc.UserManager({
response_mode: "query",
userStore: new Oidc.WebStorageStateStore({ store: window.localStorage })
})
.signinRedirectCallback()
.then(function(user) {
console.log("signin response success", user);
window.location.href = "/";
})
.catch(function(err) {
console.log(err);
});
</script>
and the error is:
Access to XMLHttpRequest at 'http://ids.example.com/connect/token' from origin 'http://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
logincb.html?code=396EA4E3D60DFF3620FAB600F3F0DB8AC7DC5CC7922341A3F8E7A3E973E10705&scope=openid profile email phone role offline_access api1.full&state=7fc1f62c72034eddab48a1cac1d261f2&session_state=rQ_HS6gus4BkM7tmL_xgJbWwLhm83nMbks1NCQYymBo.CC889AAAE84EAA20ABD2DFD643BB7797:22 Error: Network Error
at XMLHttpRequest.req.onerror (JsonService.js:179)

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 RequestedClaimTypes is Empty

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.

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