Alexa Unable to link your OAuth Skill - oauth-2.0

I am trying to link my skill to my OAuth 2.0 server.
I am using "Auth Code Grant".
In my server I receive from Alexa this:
{ client_id: 'alexa-skill',
response_type: 'code',
state: 'eyJpbml0VmVjdG9yIjoiTWYyemxRaGh6MGtScm9wUW8rRlE4dz09IiwicGF5bG9hZCI6InFQOTBDNW1TY2xueEcxbXFpaEpkcEJqYjRBNTFOa0VIazJHSjJLWERHL1NYQTRaa0tQL1MwMk5lVUlYSkE4NExXdVhXdkx5Q0JWVU52R28waTV0UkRqOGxZVk4rdEpxVmVEQ0ljT1hldXBnWkRseWZFUHY5MmJqb05CSk9ZWjFSYnh5ck5WdzB6NTRmOWU4a2JwRWJHMGVGRWJXS0ErMmhQSzRlajFLT3pjY2dxeVNxNm1hNS9UWENoU3lwYTJRS1k3ekdnN2VXbFNhd2FhN0twVjVhVlltbDRnMVFBY2h1QnBZVjdIUnEzVm5RVnVTY0JFVnlpbFJnOXludFIyRWpJY3JwTTArNjlESW53L3lqY05mdEJSaHdjSTNSQzdVbjd0TDQ5Q1NnZjB1cGFXS3pWNElFbWFZTVRPWERFc3d5cC9nWjd0eDF1SExnaHpEcCtCSVpNWXdzOWMrK2xib2N1YWl0UmhzaUFGbjJxTmxwV0ZOQzh4TkE5OXhVcUJxSVA0dDJXSkhCdUxnLzJzakJtMG1Wd0lySzFBUUhrUkkrVE5Cc0ROemFnUGhMVjdRQzgyZVdneG1EeFdVSThHZFNkRjJEL2ozb0NNYi83b1pGMlp3SXhIWG9WTGJ6azJOWGNwTk5WUkVzQ3ByODgyYW1MeFR3SUlhUWFQL1VVRFQydy85ckxBenVDUi9pN2UxREZxZW5kaXJzRXlwSnJ0RDQ4Wm05ZmtpTzR5YktiSUYwY2RPVU8vMi8yclJpcmZ5ZWFOWmt6SVNJNkRLUi9mQllXc0FVR3M2MXU0WTlaaktTc21DNCtPUWxieFRhOE1GeWFDNUFiaWJoLzFYR2xEQUpsaHIrVEloeWxrUDU0N1ltNWU4MGQyYndxSkxmVmNtcU1OelFHQUtkMWV3VjZyd3hwMXJiWTlnOGNVT3pqWUtYc05jMlRaSUVpUVN4U2FzQ2MxRVVQKzV1aldpeTdYMWlCb0VYZEtBRTBlQzM3MTFvbGlZNVVjL0FDS1pOWW1KUTBqUHJDdmpjU0Uxbk5KT2toaFJJOEhzUnArdjZlYmdWc2V2aDUwWk9aVm9RNlQ5bU5UZmhONzBDVFRVMjNLeS92am4vaE42c1N2YzNNeW4xT0lJMmNkODRRQVp5TFZJVkwwcjhWVWg0TFV2UTdaazV6ZW1OaTRDTTl0Vi95T2RiMUJkUGhSZ2hHQVozU1VOR3Z5aDA5QT09IiwidmVyc2lvbiI6MX0',
redirect_uri: 'https://pitangui.amazon.com/api/skill/link/1111111111' }
After this I try to redirect to following url:
https://pitangui.amazon.com/api/skill/link/1111111111?state=eyJpbml0VmVjdG9yIjoiTWYyemxRaGh6MGtScm9wUW8rRlE4dz09IiwicGF5bG9hZCI6InFQOTBDNW1TY2xueEcxbXFpaEpkcEJqYjRBNTFOa0VIazJHSjJLWERHL1NYQTRaa0tQL1MwMk5lVUlYSkE4NExXdVhXdkx5Q0JWVU52R28waTV0UkRqOGxZVk4rdEpxVmVEQ0ljT1hldXBnWkRseWZFUHY5MmJqb05CSk9ZWjFSYnh5ck5WdzB6NTRmOWU4a2JwRWJHMGVGRWJXS0ErMmhQSzRlajFLT3pjY2dxeVNxNm1hNS9UWENoU3lwYTJRS1k3ekdnN2VXbFNhd2FhN0twVjVhVlltbDRnMVFBY2h1QnBZVjdIUnEzVm5RVnVTY0JFVnlpbFJnOXludFIyRWpJY3JwTTArNjlESW53L3lqY05mdEJSaHdjSTNSQzdVbjd0TDQ5Q1NnZjB1cGFXS3pWNElFbWFZTVRPWERFc3d5cC9nWjd0eDF1SExnaHpEcCtCSVpNWXdzOWMrK2xib2N1YWl0UmhzaUFGbjJxTmxwV0ZOQzh4TkE5OXhVcUJxSVA0dDJXSkhCdUxnLzJzakJtMG1Wd0lySzFBUUhrUkkrVE5Cc0ROemFnUGhMVjdRQzgyZVdneG1EeFdVSThHZFNkRjJEL2ozb0NNYi83b1pGMlp3SXhIWG9WTGJ6azJOWGNwTk5WUkVzQ3ByODgyYW1MeFR3SUlhUWFQL1VVRFQydy85ckxBenVDUi9pN2UxREZxZW5kaXJzRXlwSnJ0RDQ4Wm05ZmtpTzR5YktiSUYwY2RPVU8vMi8yclJpcmZ5ZWFOWmt6SVNJNkRLUi9mQllXc0FVR3M2MXU0WTlaaktTc21DNCtPUWxieFRhOE1GeWFDNUFiaWJoLzFYR2xEQUpsaHIrVEloeWxrUDU0N1ltNWU4MGQyYndxSkxmVmNtcU1OelFHQUtkMWV3VjZyd3hwMXJiWTlnOGNVT3pqWUtYc05jMlRaSUVpUVN4U2FzQ2MxRVVQKzV1aldpeTdYMWlCb0VYZEtBRTBlQzM3MTFvbGlZNVVjL0FDS1pOWW1KUTBqUHJDdmpjU0Uxbk5KT2toaFJJOEhzUnArdjZlYmdWc2V2aDUwWk9aVm9RNlQ5bU5UZmhONzBDVFRVMjNLeS92am4vaE42c1N2YzNNeW4xT0lJMmNkODRRQVp5TFZJVkwwcjhWVWg0TFV2UTdaazV6ZW1OaTRDTTl0Vi95T2RiMUJkUGhSZ2hHQVozU1VOR3Z5aDA5QT09IiwidmVyc2lvbiI6MX0&code=Akasa7I91495468793321
Alexa APP load page with message "Unable to link your OAuth Skill"
What I am doing wrong?

Verify if the token returned from the Oauth2.0 system is in this format :
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
Please refer to this Oauth2.0 documentation here

Related

Regarding Microsoft Graph API integration in Angular - Spring Boot application

Graph API request for which you are seeing the problem
https://graph.microsoft.com/v1.0/me/messages
Graph API error message
{
'error': {
'code': 'InvalidAuthenticationToken',
'message': 'Access token validation failure. Invalid audience.',
'innerError': {
'request-id': '12e4940d-58af-4d64-98ab-4b3fe645afb8',
'date': '2020-05-05T13:57:38'
}
}
}
Description :
Implementing use case where user can extract emails in our application to download and consolidate attachments to central location using Microsoft graph.
Web application fronted is developed in angular and back-end is developed in spring boot REST API.
Integrating MSAL in angular to authenticate user and get valid tokens.
code configuration :
MsalModule.forRoot({
clientID: '83de5e6f-6a5b-48f4-8b64-5e8d6e70aa9d',
authority: 'https://login.microsoftonline.com/common/',
redirectUri: 'http://localhost:4200/TestLawyer/mail',
cacheLocation: 'localStorage',
// storeAuthStateInCookie: isIE, // set to true for IE 11
popUp: true,
consentScopes: ['user.read'],
unprotectedResources: ['https://www.microsoft.com/en-us/'],
protectedResourceMap: protectedResourceMap,
// logger: loggerCallback,
correlationId: '1234',
piiLoggingEnabled: true
})
I got the tokens in localstorage. In my solution i want to pass token to our spring boot rest server to extract emails using Microsoft Graph.
can you please help me on this like any tutorials or guidance to implement this use case.
The resource or scope of your token is incorrect.
You should set https://graph.microsoft.com/.default in the consentScopes.
You can decode your access token in https://jwt.io to see if the token includes the correct resource/scope.

invalid_scope error in access token for client credential flow

I am getting invalid_scope error in access token request for client credential flow. The error log states that "cannot request OpenID scopes in client credentials flow". I haven't requested for the open id scope. I don't know from where it came from. I need to generate access token using client credential flow.
Issue / Steps to reproduce the problem
API Resource definition.
public IEnumerable GetApiResources()
{
return new List {
new ApiResource
{
Name = "WidgetApi",
DisplayName = "Widget Management API",
Description = "Widget Management API Resource Access",
ApiSecrets = new List { new Secret("scopeSecret".Sha256()) },
Scopes = new List {
new Scope("WidgetApi.Read"),
new Scope("WidgetApi.Write")
}
}
};
}
Client Definition;
return new List
{
new Client
{
ClientId = "WidgetApi Client Id",
ClientName = "WidgetApi Client credential",
RequireConsent = false,
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret( clientSecret.Sha256())
},
// scopes that client has access to
AllowedScopes = { "WidgetApi.Read", "WidgetApi.Write"},
AccessTokenLifetime = 3600
};
}
Access token request body (key - value) using postman
grant_type client_credentials
response_type id_token
scope WidgetApi.Read WidgetApi.Write
client_secret xxxxxxxxxxxxxxxxxxxxxx
client_id WidgetApiClientId
Relevant parts of the log file
dbug: Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerConnection[4]
Closing connection to database 'IdentityServer4Db' on server 'localhost\SQLEXPRESS'.
dbug: IdentityServer4.EntityFramework.Stores.ResourceStore[0]
Found PssUserMgtApi.Read, PssUserMgtApi.Write API scopes in database
fail: IdentityServer4.Validation.TokenRequestValidator[0]
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx cannot request OpenID scopes in client credentials flow
fail: IdentityServer4.Validation.TokenRequestValidator[0]
{
"ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"ClientName": "xxxxxxxxxxxxxxxxxxxxxxxxx",
"GrantType": "client_credentials",
"Scopes": "xxxxxxxxxx.Read xxxxxxxxxxxxx.Write",
"Raw": {
"grant_type": "client_credentials",
"response_type": "id_token",
"scope": "xxxxxxxxxxxx.Read xxxxxxxxxxxxx.Write",
"client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
}
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 5292.2873ms 400 application/json
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
Connection id "0HL51IVGKG792" completed keep alive response.
Since there is no user tagged in a client credential flow, normally, client credential is not intended to have a scope tagged to it, and many frameworks doesnt support it.
https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/ says :
scope (optional) :
Your service can support different scopes for the client credentials grant. In practice, not many services actually support this.
Check whether your client credential details are correct or not. You can also find this simple step by step explanation to configure client credential flow using this link
If you have this problem, just remove the 'openid' scope for a given client in the database in ClientScopes.
Actually the question already contains the answer:
grant_type client_credentials
response_type id_token
scope WidgetApi.Read WidgetApi.Write
client_secret xxxxxxxxxxxxxxxxxxxxxx
client_id WidgetApiClientId
The request of client_credentials type should be processed at token endpoint and must not require id_token as the flow is non-interactive. The redundant parameter is breaking the flow.
I get this error with IdentityServer4 2.1.3, but not with IdentityServer4 2.3.2. It seems, from the GitHub issues for the project, that it was fixed in 2.3:
https://github.com/IdentityServer/IdentityServer4/issues/2295#issuecomment-405164127

Microsoft Graph API access token validation failure

I use this URL to get id_token:
https://login.microsoftonline.com/common/oauth2/authorize?
response_type=id_token%20code&
client_id=MY_CLIENT_GUID_ID_IN_HERE&
redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fopenid%2Freturn&nonce=alfaYYCTxBK8oypM&
state=6DnAi0%2FICAWaH14e
and this return result like this
http://localhost:3000/auth/openid/return?
code=AAA_code_in_here&
id_token=eyJ0eXAi_xxxx_yyyy_in_here&
state=6DnAi0%2FICAWaH14e&
session_state=xxxx_guid_xxxxx
and then i use the id_token to query Graph (use POST man)
i have see this post InvalidAuthenticationToken and CompactToken issues - Microsoft Graph using PHP Curl but make no sense.
OATH 2.0 requires multiple steps. The first request returns an OAUTH Code. The next step is converting that OATUH code into a Bearer Token. This is the step you are missing here.
I would also recommend using the v2 Endpoint which is a lot easier to work with (particularly with Graph). I wrote a v2 Endpoint Primer that walks through the process and may be helpful as well.
You can't use the token directly, there is one more step to exchange the code you get from the response url into token.
Here is my C# code (using Microsoft.IdentityModel.Clients.ActiveDirectory)
public static AuthenticationResult ExchangeCodeForToken(string InTenantName, string InUserObjId, string InRedirectUri, string InApplicationAzureClientID, string InApplicationAzureClientAppKey)
{
Check.Require(!string.IsNullOrEmpty(InTenantName), "InTenantName must be provided");
Check.Require(!string.IsNullOrEmpty(InUserObjId), "InUserObjId must be provided");
if (CanCompleteSignIn) //redirect from sign-in
{
var clientCredential = new ClientCredential(InApplicationAzureClientID, InApplicationAzureClientAppKey);
var authContext = new AuthenticationContext(Globals.GetLoginAuthority(InTenantName), (TokenCache)new ADALTokenCache(InUserObjId)); //Login Authority is https://login.microsoftonline.com/TenantName
return authContext.AcquireTokenByAuthorizationCode(VerificationCode, new Uri(InRedirectUri), clientCredential, Globals.AZURE_GRAPH_API_RESOURCE_ID); //RESOURCE_ID is "https://graph.microsoft.com/"
}
return null;
}
I had this issue today when I was playing with graph API, the problem in my case was how I was generating the token.
I used postman for generating the token wherein the Auth URL section I was adding the resource = client_id whereas it should be the graph URL. After making that change I was able to make the call via postman.
In order for the above to work, please make sure your application in Azure has delegated permissions to access the Graph API.
To receive the access token and use it for profile requests, you don't need anything from server-side, you can implement the oAuth2 just from the client side.
Use the following URL for login:
https://login.microsoftonline.com/common/oauth2/authorize?client_id=YOUR_CLIENT_ID&resource=https://graph.microsoft.com&response_type=token&redirect_uri=YOUR_REDIRECT_URI&scope=User.ReadBasic.All
After successful login, user will redirected to the page with access_token parameter. Then use the following AJAX call to fetch user info:
var token = login_window.location.href.split('access_token=').pop().split('&')[0];
$.ajax({
url: "https://graph.microsoft.com/v1.0/me",
type: "GET",
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Bearer '+token);},
success: function(data) {
alert('Hi '+data.displayName);
console.log(data);
}
});
Note that you may need to enable oauth2AllowImplicitFlow:true setting from your Azure Active Directory application manifest file.
Set "oauth2AllowImplicitFlow": false to "oauth2AllowImplicitFlow": true.
Lastly, ensure that your app has required permissions for Microsoft Graph which are sign in users and View users' basic profile
An updated answer to get access with new applications:
Register your app in the app registration portal.
Authorization request example:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?client_id=6731de76-14a6-49ae-97bc-6eba6914391e&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&response_mode=query&scope=offline_access%20user.read%20mail.read&state=12345
Authorization response will look like this:
https://localhost/myapp/?code=M0ab92efe-b6fd-df08-87dc-2c6500a7f84d&state=12345
Get a token
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=user.read%20mail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps
Use the access token to call Microsoft Graph
GET https://graph.microsoft.com/v1.0/me
Authorization: Bearer eyJ0eXAiO ... 0X2tnSQLEANnSPHY0gKcgw
Host: graph.microsoft.com
Source:
https://learn.microsoft.com/en-us/graph/auth-v2-user?context=graph/api/1.0
You can also get an access token without a user, see here:
https://learn.microsoft.com/en-us/graph/auth-v2-service

Can't hit Google plus api after oauth with Firebase

2 hours trying to get this to work and I can't. Firebase authenticates the user just fine, but then it can't fetch anything from the Google Plus API.
The error you will get:
{
domain: "global"
location: "Authorization"
locationType: "header"
message: "Invalid Credentials"
reason: "authError"
}
The code is this:
Auth.$authWithOAuthPopup(provider, {
scope: ['profile', 'email']
}).then(function(authData) {
console.log(authData.token);
gapi.client.setApiKey('<APIKEY>');
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(resp) {
console.log('Retrieved profile for:' + resp.displayName);
debugger;
});
});
}, showError);
It must have something to do with Firebase making the call on our behalf. Because this codepen, in which we do our own authentication, works fine:
http://codepen.io/morgs32/pen/KVgzBw
Don't forget to set clientId and apiKey in the codepen.
If you can figure this one out you're gonna get gold on christmas.
You're trying to use authData.token to access Google. But authData.token is a JWT token for accessing Firebase.
To access Google, you should use authData.google.accessToken.
Also see this page in the Firebase documentation on using the Google provider.

How can I refresh a google plus bearer token (javascript)?

I'm using the google HTML sign-in button in my single page (javascript) application to obtain an authorization object from users with Google logins. This is detailed here: https://developers.google.com/+/web/signin/add-button.
I successfully receive back a token such as shown below. Since this token expires in 1 hour, I need to refresh the token every 30 minutes or so, until the user choses to log out. I am attempting this by calling:
gapi.auth.authorize({client_id: "90... ...92.apps.googleusercontent.com", scope: "profile email", immediate: true}, function() { console.log( arguments ); } );
but with no luck. I receive the same token back until it expires, after which I get back the empty (not signed in) token. How can I preserve / refresh the bearer token without the user having to continually log in again?
{
_aa: "1"
access_token: "ya29.1.AA... ...BByHpg"
authuser: "0"
client_id: "90... ...92.apps.googleusercontent.com"
code: "4/Nyj-4sVVcekiDnIgMFh14U7-QdRm.svPMQSODiXMbYKs_1NgQtmX9F90miwI"
cookie_policy: "single_host_origin",
expires_at: "1398341363",
expires_in: "3600",
g_user_cookie_policy: undefined,
id_token: "eyJhbGciOiJ... ...0Es1LI"
issued_at: "1398337763",
num_sessions: "2",
prompt: "none",
response_type: "code token id_token gsession",
scope: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
session_state: "b92d67080... ...73ae",
state: "",
status: {
google_logged_in: true,
method: "AUTO",
signed_in: true
},
token_type: "Bearer"
}
Using the client side flow (ie Java Script) you can only receive short-lived (~1 hour) access_token. If you want to be able to refresh it, you need a refresh_token which can only be obtained using the server side flow.
You can find more information here.
Basically,it works like this :
The user connects to your Website and clicks on the "Sign-in button"
You receive an access_token and a code in JavaScript
You send this code to a PHP Script on your web server
The script makes a request to Google Servers and exchanges your code for an
access_token(which should be identical to the one you just received in JavaScript) and a refresh_token
You need to store this refresh_token somewhere (in a data base for
example) because it will only be issued once (when the users grants
permission)
When one of your access_token is about to expire, you can use your
refresh_token to get another valid access_token
As well as setting a timer, you should check that your token is still valid before making the API call. Now that the client library returns promises, and promises are chainable, you can do it really elegantly.
See my gist here.

Resources