Google Home App, cannot get OAUTH working properly - oauth-2.0

We are building a smart home app using actions on google for the google home. Our app requires signing into our system to be able to access the users devices so they can control them using their voice over google home. Our user backend is built using AWS Cognito. We are using API.AI as part of the Google Home app.
I have configured the Cognito OAUTH2 endpoint and the actions on google app to work with each other using the auth code flow and varying scope's but there is something I am missing. When I attempt to link the user account to the Google Home app i get redirected to our login page. After filling out the user details I'm returned to the Google Home 'Discover' tab but there is a message across the bottom that states: ‘Bad response from IdP in Auth Code Exchange’.
I also have tried it using Google's OAUTH2 playground. It seems that when using that I am able to get the code from our OATUH server, but when trying to exchange the code for a token i get the following error:
HTTP/1.1 400 Bad Request
Strict-transport-security: max-age=31536000 ; includeSubDomains
X-content-type-options: nosniff
X-application-context: application:prod:8443
Transfer-encoding: chunked
Set-cookie: XSRF-TOKEN=35f58337-76f4-4993-a0c9-93429134ea42; Path=/; Secure; HttpOnly
Expires: 0
Server: Server
Connection: keep-alive
X-amz-request-id: 284d862e-b021-4079-b5f5-3cbce675983c
X-xss-protection: 1; mode=block
Pragma: no-cache
Cache-control: no-cache, no-store, max-age=0, must-revalidate
Date: Wed, 23 Aug 2017 13:51:42 GMT
X-frame-options: DENY
Content-type: application/json;charset=UTF-8
{
"error": "invalid_client"
}
I have checked and rechecked the client ID and client secret etc and cannot find any errors.
Does anyone have any idea how I might fix this problem?
Thanks in advance

ok,may be i know the reason.....If you use aws cognito ...
According to this doc (http://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html)
Authorization
If the client was issued a secret, the client must pass its client_id and client_secret in the authorization header through Basic HTTP authorization. The secret is Basic Base64Encode(client_id:client_secret).
they need put client and client sectet in header ...
Then I use aws http proxy caught the request of google progress .
Method request headers: {X-Cloud-Trace-Context=d7b6b9b8239965baf69acab659e80a01/13879251242019662389, CloudFront-Viewer-Country=US, CloudFront-Forwarded-Proto=https, CloudFront-Is-Tablet-Viewer=false, CloudFront-Is-Mobile-Viewer=false, User-Agent=google-oauth-playground AppEngine-Google; (+http://code.google.com/appengine; appid: s~oauth2playground), X-Forwarded-Proto=https, CloudFront-Is-SmartTV-Viewer=false, Host=en75z5h2rb.execute-api.us-east-1.amazonaws.com, Accept-Encoding=gzip, deflate, X-Forwarded-Port=443, X-Amzn-Trace-Id=Root=1-5a0fcef2-09197cd86a625ad47d78f0b7, Via=1.1 d63a8908759a2f4775b3f672ebf823cc.cloudfront.net (CloudFront), X-Amz-Cf-Id=nFdLK97vAS5HvmpNYkPpbUMOB4bCaM6pScHWTAReAnonLg1gXF7hSg==, X-Forwarded-For=107.178.195.199, 54.182.238.53, content-type=application/x-www-form-urlencoded, CloudFront-Is-Desktop-Viewer=true}
There are no Authorization in request header. So the Cognito will return back
"error": "invalid_client"
According this OAUTH2.0 spec (https://www.rfc-editor.org/rfc/rfc6749#section-2.3.1)
I have already ask AWS support. They said:
Thanks for contacting AWS Support and providing us with detailed references. I would be happy to assist with your question regarding Cognito supporting client credentials in the request-body.
After reading through the OAUTH2.0 Standards RFC 6749 [0], It looks like including the client credentials in the request-body is not recommended. Here's an excerpt on the spec:
"Including the client credentials in the request-body using the two parameters is not recommended and should be limited to clients unable to directly utilize the HTTP Basic authentication scheme (or other password-based HTTP authentication schemes)."

Related

Access Azure DevOps API via OAuth 2.0 Azure AD

In postman i have set simple request to access Azure DevOps API, using OAuth 2.0 security via Azure AD
https://learn.microsoft.com/en-us/rest/api/azure/devops/core/projects/list?view=azure-devops-rest-5.1
On Azure AD application setup with permission to acess Azure DevOps API with user consent
Im getting token, however Azure DevOps API keeps returning code 203 with sign in html instead of json response. I would appreciate any suggestions
Thanks
Postman request
GET /[some_org]/_apis/projects?api=5.1 HTTP/1.1
Host: dev.azure.com
Authorization: Bearer [something]
User-Agent: PostmanRuntime/7.17.1
Accept: */*
Cache-Control: no-cache
Postman-Token: [something]
Accept-Encoding: gzip, deflate
Cookie: VstsSession=[something]
Referer: https://dev.azure.com/[some_org]/_apis/projects?api=5.1
Connection: keep-alive cache-control: no-cache
Token get details
I managed to replicate your issue using the OAuth 2.0
authentication in Postman.
I started to look little more about the REST API for Azure DevOps
and I found the document:
https://learn.microsoft.com/en-us/rest/api/azure/devops/?view=azure-devops-rest-5.1
Few key parts:
For Azure DevOps Services, instance is dev.azure.com/{organization},
so the pattern looks like this:
VERB https://dev.azure.com/{organization}/_apis[/{area}]/{resource}?api-version={version}
If you wish to provide the personal access token through an HTTP
header, you must first convert it to a Base64 string (the following
example shows how to convert to Base64 using C#). (Certain tools like
Postman applies a Base64 encoding by default. If you are trying the
API via such tools, Base64 encoding of the PAT is not required).
Authorization: Basic BASE64PATSTRING
I configured my postman to use GET Request and Basic Authentication (and it worked)
GET http://dev.azure.com/{organization-id}/_apis/projects?api-version=5.1
More information about Personal Access Token for Azure DevOps REST API is available
here
PS
And here is page regarding Azure DevOps REST API and OAuth 2.0 Authentication
here
There is information on how to register your application to generate OAuth 2.0 credentials required to authorize.

OAuth 2.0 Playground: unauthorized_client

I've tried several times, but I can't use the Google PlayGround tool with the Google People API v1.
Request / Response
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-length: 278
content-type: application/x-www-form-urlencoded
user-agent: google-oauth-playground
code=4%2FhQlhA-MiWKhcmHWVUddb8TmiaVEDdMd_3lDHid9eYBc&redirect_uri=https%3A%2F%2Fdevelopers.google.com%2Foauthplayground&client_id=111243977462-pc15rhq33ojuc7i54ce3qd8upj6mtnc3.apps.googleusercontent.com&client_secret=ozWubBNz1iKdykitcK757UOo&scope=&grant_type=authorization_code
HTTP/1.1 401 Unauthorized
Content-length: 74
X-xss-protection: 1; mode=block
X-content-type-options: nosniff
Transfer-encoding: chunked
Expires: Sun, 16 Jul 2017 14:54:42 GMT
Vary: Origin, X-Origin
Server: GSE
-content-encoding: gzip
Cache-control: private, max-age=0
Date: Sun, 16 Jul 2017 14:54:42 GMT
X-frame-options: SAMEORIGIN
Alt-svc: quic=":443"; ma=2592000; v="39,38,37,36,35"
Content-type: application/json; charset=UTF-8
Www-authenticate: Bearer realm="https://accounts.google.com/"
{
"error_description": "Unauthorized",
"error": "unauthorized_client"
}
I've already followed the tips below:
1. Delete the whitespace in the 'OAuth Client ID' and 'OAuth Client secret' in the OAuth 2.0 configuration of Google PlayGround
2. Define in the manager API the authorized redirection URI for: https://developers.google.com/oauthplayground
3. Verify in the Manager API is enabled on the Dashboard
Could someone help me with any more tips for me to try to solve the problem?
Below is a description of what I did on Google Playground:
Step 1 Select & authorize APIs
1. I select Google API v1 and framework https://www.googleapis.com/auth/contacts.readonly
2. Click the 'Authorize APIs'
Step 2 Exchange authorization code for tokens
1. Click the 'Oauth 2.0 Configuration'
2. Click on the 'Use your own OAuth credentials'
3. Enter the 'OAuth Client ID' and the 'OAuth Client secret'
4. Click the button: 'Exchange authorization code for tokens'
I do not know if it has to do with the issue, but I noticed that the list of applications connected to my account does not appear 'OAuth 2.0 Playground'. But I have the 'Google APIs Explorer' where I successfully tested access to my contacts (Google People API).
Go to settings by clicking the gear icon. Set the following as defined below and then tick the 'Use you own Oauth credentials' and fill in the client_id and client_secret of your Google OAuth app :
Add google.com in the authorized domain list of your app's OAuth consent screen.
Click on the application for which you want to configure for the next step:
Make sure to also add 'https://developers.google.com' in the Authorized JavaScript origins and 'https://developers.google.com/oauthplayground' in the Authorized redirect URIs[click 'save' below once added]:
Then click 'Authorize APIs' after selecting the appropriate access requirements:
You should get a prompt and then authorize it [incase of a safety warning, proceed ahead and click allow for the permissions requested]
Once done you should have an authorization code using which you can generate your token[access token and refresh token, we get a refresh token as we specified 'offline' in the access-type settings earlier]. This is a one time auth code[you would get an invalid_grant if you try to re-use it], store the access token and refresh token for talking to google APIs, new access token can be generated using the refresh token.

How to get new access token in OpenID Connect/OAuth2 Implicit Flow

I am currently using OpenID Connect/Oauth2 Implicit Flow in a mobile app. I am bringing up a Web View for the user to login and obtaining the access token and expiry. However, when the access token expires, do I need to ask the user to log in again? Or is there a way to get a new access token silently using the current one, without bugging the user. I guess another option is to set the token expiry to be a really long time, but I have read that this is a bad idea.
Am I missing something here?
Since Implicit flow does not send a refresh token (as explained in section 9 of RFC6746), usage of refresh tokens is not possible. But as a workaround one can use client credential grant to obtain an access token.
A viable solution is to first follow the implicit flow and authenticate the client. Then client authentication grant can be used to do the required API calls.
Sample request (from RFC6749)
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
rant_type=client_credentials
Sample resposne (from RFC6749)
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
P.S -
If you are using authorization code flow, you can use refresh_token to get a new access token. How the request should be formed can be obtained from OAuth2 documentation. Note that to do so, your authorization response should contain a `refresh_token.
A refresh token should be protected as valuable as a credential for a user. More can be read from keycloak documentation from here
Sample request and a response (from RFC6749)
Request
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
Response
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "TlBN45jURg",
"token_type": "Bearer",
"refresh_token": "9yNOxJtZa5",
"expires_in": 3600
}
The recommended way is to use an external browser and the Authorization Code Flow. Check the OAuth 2.0 for Native Apps RFC. For Android, there is also a supporting library AppAuth. With this flow, you can use a refresh token to get a new access token, but there is an issue with a client secret (usually needed for accessing /token endpoint), because you cannot keep it safe in a mobile app (it's described in the RFC).
If you decide to stick with the WebView and the Implicit Flow, which is not safe (your app can see the password) you could use the same technique as in JavaScript applications - request a new token with /auth?...&prompt=none URL which will return a new token without asking the user for credentials if he still has an open session there.

403 Error with WiFi Hotspots

I have an iOS app that connects to a Django server via XMLRPC (the protocol doesn't really matter). Authentication is persisted with standard cookies and is transparently handled by AFNetworking via NSURLConnection. The Django XMLRPC library returns a 403 error when a request fails authentication, and when this happens the app displays a login page to the user. This approach has worked fine for a few years.
A recent problem came up with a user who connected to a WiFi hotspot but did not authenticate with the hotspot. They loaded the app and was shown the login page, which must have been the result of a 403 error coming back from the hotspot. If they first authenticate with the hotspot then everything works fine.
I'm trying to find a solution to prevent my app from interpreting such 403 errors as a failed authentication. I assume one of two things are happening:
the hotspot redirects to a URL that returns a 403 error; or
the hotspot immediately returns a 403 error.
The first scenario is easy to manage by blocking 301 and 302 redirects. Fortunately, AFNetworking makes this easy.
The second scenario is more difficult and I see no way of knowing from where the 403 originated.
Does anyone know if there is anything else in the header to detect the origin of a 403, or if there is a standard practice of redirecting the request when not signed into a hotspot?
Edit
Here are what my Django service header looks like on a 403:
HTTP/1.1 403 FORBIDDEN
Server: nginx
Date: Sun, 28 Apr 2013 07:21:34 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: keep-alive
Vary: Cookie
Set-Cookie: sessionid=5a5ce154d1229e40c3773e8f6999a32d; expires=Sun, 18-Aug-2013 07:21:34 GMT; httponly; Max-Age=9676800; Path=/
The simplest solution I could find was to add a custom header to the responses from my Django server, and validate the header in the client to assure it came from the server and not the hotspot.

YouTube API refresh token revoked with 400 code "invalid_grant" (for seemingly no reason)

This is my first post on stackoverflow. Here it goes.
I've built a server-side PHP application that involves reading/making changes to one users's YouTube account (changes to caption files). The user is authenticated with OAuth 2. I have been storing the refresh_token and making refresh requests successfully when the access_token expires.
But now, I seem to be getting an error, which coincidentally correlates with two things:
User's upload of a new video
Sunday nights
I don't know if that means anything.
The error happens when trying to refresh the access token and I'm using the same way of refreshing the token as I have previously. Here are the details:
Error message:
[status code] 400
[reason phrase] Bad Request
[url] https://accounts.google.com/o/oauth2/token
[request] POST /o/oauth2/token HTTP/1.1
Host: accounts.google.com
User-Agent: Guzzle/2.8.6 curl/7.24.0 PHP/5.3.10
Content-Type: application/x-www-form-urlencoded
client_id=442147492209.apps.googleusercontent.com&client_secret=D7eLQ5b0Mo1Y8uZ30ReWYwls&grant_type=refresh_token&refresh_token=1%2FCLvAt8V_d9sZznpg5YZdJlOJ58ufbHKL4F5Lw8PiJOg
[response] HTTP/1.1 400 Bad Request
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Date: Tue, 02 Oct 2012 16:28:24 GMT
Content-Type: application/json
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Transfer-Encoding: chunked
{
"error" : "invalid_grant"
}
If you feel like looking at the source code, it's on github. Here's the relevant line number where refresh takes place: https://github.com/wellcaffeinated/yt-subtitle-explorer/blob/master/app/YTSE/OAuth/LoginManager.php#L330
(You'll notice that I've added a check for this error and ask the administrator to reauthorize the application... but this is far from ideal)
Other posts I've looked into were telling people to use approval_prompt=force... so I am doing that.
Edit:
My newest suspicion is that since I am requesting offline access (approval_prompt=force) every time the administrator logs in, I keep generating new refresh_tokens (which I don't record unless no others are available). Does google's OAuth have a maximum number of "active" refresh_tokens per application? Or something like that?
Thanks!
please check this from google developers pages:
If you receive an invalid_grant error response when attempting to use
a refresh token, the cause of the error may be due to the following
reasons:
Your server's clock is not in sync with NTP.
The refresh token limit
has been exceeded. Applications can request multiple refresh tokens to
access a single Google Analytics account. For example, this is useful
in situations where a user wants to install an application on multiple
machines and access the same Google Analytics account. In this case,
two refresh tokens are required, one for each installation. When the
number of refresh tokens exceeds the limit, older tokens become
invalid. If the application attempts to use an invalidated refresh
token, an invalid_grant error response is returned. The limit for each
unique pair of OAuth 2.0 client and Google Analytics account is 25
refresh tokens (note that this limit is subject to change). If the
application continues to request refresh tokens for the same
Client/Account pair, once the 26th token is issued, the 1st refresh
token that was previously issued will become invalid. The 27th
requested refresh token would invalidate the 2nd previously issued
token and so on.

Resources