Google OAuth2 on Mac Catalyst App: "invalid_grant" when exchanging authorization_code with refresh_token - google-oauth

We've an app that successfully performs Google sign-in from different clients: web, iOS app, Android app.
We're now trying to integrate Google Sign-in on a Mac Catalyst version of our iOS app.
We use the hybrid flow, we generate the authorization code on the client and exchange it on the server to obtain access and refresh token.
I found this library https://github.com/elsesiy/GAppAuth and I'm using the same credentials used for the iOS app.
The redirect to the app after the sign in through custom scheme works fine and I'm able to obtain the authorization code.
But when I share the authorization code with my server, I get an error on the request to exchange the authentication code to obtain access_token and refresh_token:
POST https://accounts.google.com/o/oauth2/token
Content-Type: application/x-www-form-urlencoded
Params:
code (sent by client)
client_id (server client id)
client_secret (server secret)
grant_type="authorization_code"
redirect_uri is not added for native client request.
Response:
400 {'error': 'invalid_grant', 'error_description': 'Bad Request'}
Please note that that the exactly same flow works on iOS.
I checked also a different type of credentials (Native clients, type: Other)
Any ideas on what's wrong and how can I fix this?
I suspect there's something that Google libraries for login do that is not implemented in GAppAuth (see https://github.com/openid/AppAuth-Android/issues/293).

Related

React Native OAuth2 and REST API authentication flow

I'm having a bit trouble understanding this predicament a REST API supporting React Native (Mobile App) with OAuth2 authentication.
I've managed to setup the OAuth2 flow and can login via OAuth2 provider. This communication is still just between the Mobile App and the 3rd party OAuth2 provider. How can I use those tokens I've obtained (and actually trust the request) to create an account in my REST API so that the server can actually generate a JWT token that will be used for future requests?
Can't seem to find an answer to this question. Would love some help with this one
Ok, I've managed to figure this one out.
To achieve what I want the Client (in this case mobile app) does the authentication flow and will receive an access_token and a refresh_token along with an id_token. The last one (id_token) contains the info about the user which the app should send to my REST API. Once the server receives it it will make an HTTP request to Google (my OAuth2 Provider) to verify that this token is in fact a valid one and issued by them.
After that I just create an account and issue my own token in response to the Mobile's App request.
It's explained here in Google Docs
Most likely all the major identity providers would follow along this path. Or, at least I'm hoping they do.

MobileFirst 7.1 OAuth Token Request

I am trying to use MobileFirst Server as a OAuth Server. The knowledge center provide detail step on how any external application can validate the token sent by Mobile client via MobileFirst Token validator end point. But not able to find proper documentation for request a new authorization token from mobile client from MFP Server.
In development environment we have a testtoken endpoint.
http://localhost:10080/OAuthExternalServer/authorization/v1/testtoken
{ "Authorization": "Bearer eyJqcG ......... }
How to get similar token from MFP production server on mobile client.
MFP Version: Server version: 7.1.0.00.20170330-0917
Thanks
With the OAuth security model, MFP server is your OAuth token provider. Upon completing the challenge server gives you an OAuth token. You need not do anything special in the ChallengeHandler for OAuth token. Refer to the samples here: https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/7.1‌​/authentication-security/
If you are looking to use OAUTH for authenticating an external resource , refer the steps here https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/7.0/authentication-security/using-mobilefirst-server-authenticate-external-resources/
Here is an example token validator which uses introspectionData to validate https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/8.0/authentication-and-security/protecting-external-resources/jtv/
Basically Introspection Endpoint can be invoked to validate the token provided by MFP server
General OAuth flow with 7.1 for a default flow can be referred here https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/7.0/authentication-security/authentication-concepts/oauth-based-security-model/ .

Error: Public clients can't send a client secret while try to get access token in Onedrive

I am trying to get OneDrive access token by following URL
https://login.live.com/oauth20_token.srf?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&redirect_uri=https://login.live.com/oauth20_desktop.srf&code=AUTHORIZATION_CODE&grant_type=authorization_code
but the response is as following
{"error":"invalid_request","error_description":"Public clients can't send a client secret."}
Can anyone explain this?
A "public client" is a mobile or desktop application (web services are "confidential clients"). MSA is giving you this response because you're redirecting to https://login.live.com/oauth20_desktop.srf. In this case, you should not be providing the client_secret value in the request, so your request should just look like this:
https://login.live.com/oauth20_token.srf?client_id=YOUR_CLIENT_ID&redirect_uri=https://login.live.com/oauth20_desktop.srf&code=AUTHORIZATION_CODE&grant_type=authorization_code
A more recent example from:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
Request an access token with a client_secret
Now that you've acquired an authorization_code and have been granted permission by the user, you can redeem the code for an access_token to the resource. Redeem the code by sending a POST request to the /token endpoint:
// Line breaks for legibility only
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=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&code_verifier=ThisIsntRandomButItNeedsToBe43CharactersLong
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps. This secret needs to be URL-Encoded.
Parameter
Required/optional
Description
tenant
required
The {tenant} value in the path of the request can be used to control who can sign into the application. Valid values are common, organizations, consumers, and tenant identifiers. For more information, see Endpoints.
client_id
required
The Application (client) ID that the Azure portal – App registrations page assigned to your app.
scope
optional
A space-separated list of scopes. The scopes must all be from a single resource, along with OIDC scopes (profile, openid, email). For more information, see Permissions and consent in the Microsoft identity platform. This parameter is a Microsoft extension to the authorization code flow, intended to allow apps to declare the resource they want the token for during token redemption.
code
required
The authorization_code that you acquired in the first leg of the flow.
redirect_uri
required
The same redirect_uri value that was used to acquire the authorization_code.
grant_type
required
Must be authorization_code for the authorization code flow.
code_verifier
recommended
The same code_verifier that was used to obtain the authorization_code. Required if PKCE was used in the authorization code grant request. For more information, see the PKCE RFC.
client_secret
required for confidential web apps
The application secret that you created in the app registration portal for your app. Don't use the application secret in a native app or single page app because a client_secret can't be reliably stored on devices or web pages. It's required for web apps and web APIs, which can store the client_secret securely on the server side. Like all parameters here, the client secret must be URL-encoded before being sent. This step is usually done by the SDK. For more information on URI encoding, see the URI Generic Syntax specification. The Basic auth pattern of instead providing credentials in the Authorization header, per RFC 6749 is also supported.
So, if creating a desktop or mobile app and if you registered your app as such in the Azure portal at https://portal.azure.com/ then if you send client_secret you would get that particular error. So you must remove it from the POST request to successfully exchange received code for authentication token and refresh token. Note that confidential web app is the only type which requires to send client_secret. Other types of registered apps or public apps should not send the client_secret.
The code is valid for 10 minutes so it must be immediately exchanged for authentication token using the request similar to the above.
Note one more thing:
Whether or not you'll need to send a client_secret depends whether or not you've registered your application in AzureAD as "web" (requires sending client_secret) or "native app" (does not require sending the client_secret). So your implementation will depend on the registration you did. You can change the type of application in AzureAD -> App Registrations -> select Authentication from the left menu. Under the Platform Configurations choose your specific platform.

How to authenticate client on multiple OAuth2 providers?

My REST API (api.example.com) is currently an OAuth2 provider and has been the support of a mobile app.
Recently we have created a new web-site(www.example.com) which will be treated like any other client. The web can authenticate on the API by using a password grant_type where a user types in her credentials. From there the client(browser) gets an access token and can than consume the API.
The problem comes that we want to authenticate clients on the web-page using Facebook.
So basically, the web should in the end be able to get an user access token to access the API by logging in via Facebook.
I've looked around and I've seen two legged and three legged OAuth2 authentication scenarios but:
How does that applies on the scenario I just described?
What is the right way of doing this?
What are the security issues I should take into account?
Here is one way to go about this:
When a client authenticates with facebook, on mobile using their SDK or for instance on the web using the authorise method, it will get a facebook access token.
If your API is also an OAuth2 provider and you want to then login the client(issue an access token) using the facebook token you just got you can do this by using extension grants(https://www.rfc-editor.org/rfc/rfc6749#section-4.5).
Here, the same way OAuth2 has a password_grant type, you could create an extension grant called, for instance facebook_access_token_grant, and send that facebook token to the API. If the token is valid than the API issues an app access token that can be used by the client on subsequent requests.
The steps are:
1 - Client gets an access token from facebook. On mobile devices will be using the Facebook SDK, on web apps can be with the javascript login thing or using the authorise method where the browser is redirected to facebook and so on.
2 - After getting the facebook access token, the client requests a access token from the API by posting:
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
client_id={{ client_id }}&client_secret={{ client_secret }}&grant_type=facebook_access_token&facebook_access_token={{ TOKEN }}
3 - API debugs the facebook_access_token with a https://graph.facebook.com/me?access_token=TOKEN.
If everything validates the API issues a bearer token to the client by responding something like:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"API_ACCESS_TOKEN",
"username":"theuser",
"expires_in":3600,
"refresh_token":"API_REFRESH_TOKEN",
}
4 - Client uses that token on subsequent calls to the API.
GET /some_endpoint HTTP/1.1
Host: server.example.com
Authorization: Bearer API_ACCESS_TOKEN
Make sure you do all this over a secure connection(TLS) so that you don't violate https://www.rfc-editor.org/rfc/rfc6749#section-1.6 and all this should be according OAuth2 protocol.
You have to have user login both and then connect them using email

How to do authentication with a single page app and API backend?

I own both, an API backend at api.example.com and a frontend single page app at example.com . The API is basically a wrapper for a database backend.
Now, I want to have a user of the single page app (= client app) to authenticate with the API. For this, as far as I understand, the client (= single page app) sends a client_id together with a user_id to the API, and the API then gives out an AccessToken.
However, in my single page app, I am not sure where/how to store the access token. I am looking for a simple reference, or a good concept to have a logged in user access the API application.
A friend suggested me this flow:
The client displays inputs for login (be it e-mail or username) and password,
The client app makes a request to your API to obtain an unauthorized token (e.g. POST /api/v1/auth/new),
The server creates a token for the app and sends it back,
The client app sends the token along with the login, password and request signature to the API (e.g. POST /api/v1/mobile_authenticate),
The API validates and verifies the credentials,
If everything is OK the app uses the token to make further on the user's behalf.
Any thoughts? How can this be simplified or improved?
I'm using the api key for the permission check in the server API. The workflow how the api key works is as below.
The client app displays inputs for login (be it e-mail or username) and
password.
The client app makes a request to the API to obtain an
api key (e.g. POST /api/v1/users/validate).
The client app accesses the API with the api key. (If it is the http request, the api key can be included in the http header or query string.)
Hope it helps.