I am getting Invalid authorization code{"code": "MyTestCode"},
Here is more detailed error:
Invalid authorization code{"code": "MyTestCode"}, details: {"ClientId": "AuthorizationCodeClientFlow", "ClientName": "Authorization Code Client", "GrantType": "authorization_code", "Scopes": null, "AuthorizationCode": "MyTestCode", "RefreshToken": null, "UserName": null, "AuthenticationContextReferenceClasses": null, "Tenant": null, "IdP": null, "Raw": {"grant_type": "authorization_code", "code": "MyTestCode", "redirect_uri": "https://localhost:5000/oauth/callback", "client_id": "AuthorizationCodeClientFlow"}, "$type": "TokenRequestValidationLog"} <s:IdentityServer4.Validation.TokenRequestValidator>
I am testing using Postman
This is client generated from this code :
{
ClientName = "Authorization Code Client",
ClientId = "AuthorizationCodeClientFlow",
AllowedGrantTypes = GrantTypes.Code,
ClientSecrets =
{
new Secret("AuthorizationCodeClientFlowSecret".Sha512())
},
AllowedScopes =
{
"all"
},
RedirectUris =
new List<string> {
"https://localhost:5000/oauth/callback"
},
AllowOfflineAccess = false,
AccessTokenLifetime = 60
};
https://localhost:5105/oauth/authorize works fine. I get error in https://localhost:5105/oauth/token step. When I validate request like that:
var form = (await _httpContextAccessor.HttpContext.Request.ReadFormAsync()).AsNameValueCollection();
var validationResult = await _requestValidator.ValidateRequestAsync(form, clientResult);
if (validationResult.IsError)
{
return new IdpTokenResponse
{
Custom = new Dictionary<string, object>
{
{ "Error", validationResult.Error },
{ "ErrorDescription", validationResult.ErrorDescription }
}
};
}
You need to take the authorization code that you receive from the initial authentication request and then take it and pass it along when you get the token from the token endpoint.
one unrelated thing is that you should always ask for the openid scope when you authenticate against IdentityServer, All or "" is not valid.
Related
Playing around with a demo project from PluralSight, I am trying to have the IDP redirect back to the server app on sign out.
The PostLogOutRedirectUris is defined in the config for the Client at the IDP level, but it doesn't seem to have any effect.
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "bethanyspieshophr",
ClientName = "Bethany's Pie Shop HRM",
AllowOfflineAccess = true,
AccessTokenLifetime = 120,
RequireConsent = false,
RequirePkce = true,
AllowedGrantTypes = GrantTypes.Code,
ClientSecrets = {
new Secret("108B7B4F-BEFC-4DD2-82E1-7F025F0F75D0".Sha256()) },
RedirectUris = { "https://localhost:44301/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:44301/signout-oidc" },
AllowedScopes = { "openid", "profile", "email", "bethanyspieshophrapi" }
}
};
If I manually at runtime set the LoggedOutViewModel it works as expected.
How are you performing the logout request? Remember that the value in the client settings is just the registered value which is verified during logout request. The user is not redirected automatically to the postLogoutRedirectUri. You have to pass a post_logout_redirect_uri parameter to the end session endpoint and this parameter must match on of the values in the PostLogoutRedirectUris setting. To use this feature you should also post a valid ID token in the id_token_hint parameter, so that the server knows which client is requesting the logout.
You can have a look at the end session enpoint docs for details.
The issue was simply due to a typo, which sent me on a wild goose chase.
PostLogoutRedirectUris = { "https://localhost:44301/signout-oidc" },
Should be
PostLogoutRedirectUris = { "https://localhost:44301/signout-callback-oidc" },
And then, it worked.
So we've set up an IDP (IdentityServer4, Core2) and have been using it for our own applications without problems (Implicit Flow). Now though, one of our partners will be using our IDP to make API requests from another application.
We've setup the ApiResources:
new ApiResource("api", "API",
new List<string>() {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Profile,
"role",
"team"
})
The client in question:
new Client {
ClientName= "ClientName",
Description = "ClientDescription",
LogoUri = "/img/ClientLogos/clientLogo.png",
ClientUri = "https://client.url",
RedirectUris = {
"https://...",
"https://...",
"http://...",
"http://..."
},
AllowedGrantTypes = GrantTypes.Code,
RequireConsent = true,
ClientId = "clientId",
AllowedScopes = { "api" },
ClientSecrets = { new Secret("clientSecret".Sha256()) },
AlwaysSendClientClaims = true,
AllowOfflineAccess = true,
Claims = {
...
}
}
I (wrongfully) assumed that since the client has the "api" scope, which in turn has the "OpenID" and "Profile" scope, the client would automatically gain authorization to use the UserInfo endpoint, but they are getting the "Forbidden" StatusCode.
Can someone explain to me what we're doing wrong here?
I think you need to include IdentityResources as well, because it dictates whats part of the ID-token and what is available from the UserInfo endpoint.
I have been trying to connect an assistant action to my backend server
I am using my own Oauth server and followed the instructions on
https://developers.google.com/actions/identity/oauth2?oauth=code
I am using actions_intent_Sign_in for my dialogflow event intent (like https://actions-on-google.github.io/actions-on-google-nodejs/classes/conversation_helper.signin.html)
when i use my action to sign in, i get the login window to my server, i do the account linking and i can see that i generated the tokens on my server but i cant find the token in (conv.user.access.token)
and this is the code for my intent using "actions on google sdk "
'use strict';
var _ = require('lodash');
var path = require('path')
var express = require('express')
var http = require('http')
const bodyParser = require('body-parser');
var expressApp = express().use(bodyParser.json());
var server = http.createServer(expressApp).listen(3000)
const {
dialogflow,
SignIn
} = require('actions-on-google');
const app = dialogflow({
debug: true,
clientId: '7b4a6dfc-4b35-11e9-8646-d663bd873d93'
});
app.intent('Start Sign-in', conv => {
conv.ask(new SignIn());
});
app.intent('Get Sign-in', (conv, params, signin) => {
console.log("get sign in ");
console.log(JSON.stringify(signin));
if (signin.status === 'OK') {
const access = conv.user.access.token
console.log("the access token is " + access);
conv.ask('Great, thanks for signing in! What do you want to do next?');
} else {
conv.ask('I wont be able to save your data, but what do you want to do next?.');
}
});
and the response comes back as
{"#type":"type.googleapis.com/google.actions.v2.SignInValue","status":"OK"}
the access token is undefined
Response {
"status": 200,
"headers": {
"content-type": "application/json;charset=utf-8"
},
"body": {
"payload": {
"google": {
"expectUserResponse": true,
"richResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Great, thanks for signing in! What do you want to do next?"
}
}
]
}
}
}
}
}
the user object of conv has only this data
"user": {
"raw": {
"lastSeen": "2019-03-20T12:46:23Z",
"locale": "en-US",
"userId": "okdhyeGSk5tofgLjEepIUrA6mmewCESY8MjklZRPvQJgv6-uybfPobwdfgtrGZJ3bE2sM9ninhst"
},
"storage": {},
"_id": "okdhyeGSk5tofgLjEepIUrA6mmewCESY8MjklZRPvQJgv6-uybfPobwdfgtrGZJ3bE2sM9ninhst",
"locale": "en-US",
"permissions": [],
"last": {
"seen": "2019-03-20T12:46:23.000Z"
},
"name": {},
"entitlements": [],
"access": {},
"profile": {}
}
i dont know where the access/refresh token can be found or if there is any requirement for the post to send from my oauth server that i missed
so finally i managed to get it working with the help of Actions on Google Support Team
the problem was me having another google account logged-in in another tab, even though i had the AoG and dialogflow agent connected with the same account
tried all using incognito window and it works
im using auth0 to get authentication for twitter, im using react native and i want to use twitter as login,
this is my code.
_loginWithAuth0Twitter = async () => {
const redirectUrl = AuthSession.getRedirectUrl();
const result = await AuthSession.startAsync({
authUrl: `${auth0Domain}/authorize` + toQueryString({
connection: 'twitter',
client_id: auth0ClientId,
response_type: 'token',
scope: 'openid',
redirect_uri: redirectUrl,
}),
});
after request auth give me this result
Object {
"errorCode": undefined,
"params": Object {
"access_token": "8uDhJTvWFxpr6GpTfioXp_8wCtqfwDsW",
"exp://127.0.0.1:19000/--/expo-auth-session": "",
"expires_in": "7200",
"scope": "openid",
"token_type": "Bearer",
},
"type": "success",
"url": "exp://127.0.0.1:19000/--/expo-auth-session#access_token=8uDhJTvWFxpr6GpTfioXp_8wCtqfwDsW&scope=openid&expires_in=7200&token_type=Bearer",
}
i only get acess_token and there is not much thing you can do with access token since twitter still using auth 1.0
i try to set rules
Get email address from Twitter
function (user, context, callback) {
// additional request below is specific to Twitter
if (context.connectionStrategy !== 'twitter') {
return callback(null, user, context);
}
const oauth = require('oauth-sign');
const uuid = require('uuid');
const url = 'https://api.twitter.com/1.1/account/verify_credentials.json';
const consumerKey = configuration.TWITTER_CONSUMER_KEY;
const consumerSecretKey = configuration.TWITTER_CONSUMER_SECRET_KEY;
const twitterIdentity = _.find(user.identities, { connection: 'twitter' });
const oauthToken = twitterIdentity.access_token;
const oauthTokenSecret = twitterIdentity.access_token_secret;
const timestamp = Date.now() / 1000;
const nonce = uuid.v4().replace(/-/g, '');
const params = {
oauth_consumer_key: consumerKey,
oauth_nonce: nonce,
oauth_signature_method: 'HMAC-SHA1',
oauth_timestamp: timestamp,
oauth_token: oauthToken,
oauth_version: '1.0',
oauth_callback:'https://pembersih.auth0.com/login/callback'
};
params.oauth_signature = oauth.hmacsign('POST',
url,
params,
consumerSecretKey,
oauthToken);
const auth = Object.keys(params).sort().map(function (k) {
return k + '="' + oauth.rfc3986(params[k]) + '"';
}).join(', ');
request.post(url, {
headers: {
'Authorization': 'OAuth ' + auth
},
json: true
}, (err, resp, body) => {
if (resp.statusCode !== 200) {
return callback(new Error('Error retrieving email from twitter: ' + body || err));
}
});
}
then i get this error
Object {
"errorCode": undefined,
"params": Object {
"error": "access_denied",
"error_description": "Error retrieving email from twitter: [object Object]",
"exp://127.0.0.1:19000/--/expo-auth-session": "",
},
"type": "success",
"url": "exp://127.0.0.1:19000/--/expo-auth-session#error=access_denied&error_description=Error%20retrieving%20email%20from%20twitter%3A%20%5Bobject%20Object%5D",
}
how can i get user token and secret token so that i can use twitter API ?
I'm tying to figure out OAuth2.0, OIDC1.0 and IdentityServer4. I've setup a test MVC Core client with only "openid" scope requested. But somehow OpenIdConnnect middleware keeps adding "profile" scope to the requested scopes. Is "profile" a mandatory scope? Should I enable it? Or what am I doing wrong here? I'd appreciate any input.
IdSrv resources:
_identityResources = new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResource
{
Name = "test_user",
UserClaims = new[] { "test_user.email" }
}
};
_apiResources = new List<ApiResource>
{
new ApiResource
{
Name = "test_api",
Scopes =
{
new Scope()
{
Name = "test_api.account.create",
UserClaims = new[] { "test_api.account.create" }
}
}
}
};
IdSrv client config:
new Client
{
ClientId = "client.mvcx",
ClientName = "MVC Core Client",
AllowedGrantTypes = GrantTypes.Hybrid,
AllowAccessTokensViaBrowser = false,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { Common.Addresses.Client + "/signin-oidc" },
PostLogoutRedirectUris = { Common.Addresses.Client },
LogoutUri = Common.Addresses.Client + "/signout-oidc",
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId
},
AllowOfflineAccess = false,
RequireConsent = false,
AlwaysIncludeUserClaimsInIdToken = true
},
MVC Client:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "cookies",
AutomaticAuthenticate = true,
ExpireTimeSpan = TimeSpan.FromMinutes(60)
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
AuthenticationScheme = "oidc",
SignInScheme = "cookies",
Authority = Common.Addresses.IdSrv,
RequireHttpsMetadata = false,
ClientId = "client.mvcx",
ClientSecret = "secret",
ResponseType = "code id_token",
Scope = { "openid" },
SaveTokens = true,
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = IdentityModel.JwtClaimTypes.Name,
RoleClaimType = IdentityModel.JwtClaimTypes.Role,
},
IdSrv error:
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
fail: IdentityServer4.Validation.ScopeValidator[0]
Invalid scope: profile
fail: IdentityServer4.Endpoints.AuthorizeEndpoint[0]
Request validation failed
info: IdentityServer4.Endpoints.AuthorizeEndpoint[0]
{
"ClientId": "client.mvcx",
"ClientName": "MVC Core Client",
"RedirectUri": "http://localhost:32579/signin-oidc",
"AllowedRedirectUris": [
"http://localhost:32579/signin-oidc"
],
"SubjectId": "anonymous",
"ResponseType": "code id_token",
"ResponseMode": "form_post",
"GrantType": "hybrid",
"RequestedScopes": "openid profile",
...
The OpenIdConnectionOptions automatically requests the openid and profile scopes (see source code), with a private setter on the Scope property.
When you set scopes like you are, you are not setting a new list, but adding to the existing.
Clearing and then adding the scope works:
var options = new OpenIdConnectOptions();
options.Scope.Clear();
options.Scope.Add("openid");
app.UseOpenIdConnectAuthentication(options);