I am developing a REST API secured via OAuth2 with Spring that will be used from an Android application (the client). In order to access any endpoint of my API, a OAuth2 access token is required and handed over to the endpoint via Authorization Header in a way similar to this:
"Authorization" - "Bearer accesstokenhere"
In order to acquire an access token, a username and password must be provided, as well as a client ID and client secret (they represent the Android app). The clientID and client secret are handed over to the token endpoint via Authorization Header in a way similar to this, which is specified by Spring:
"Authorization" - "Basic clientId:clientSecret"
If the client ID and client secret match a client defined on the server and if the user exists and the password is correct, access token and refresh token are returned.
Now my question is how I can securely store my clientId and client secret inside the Android application, making sure someone who reverse engineers my app does not get access to them?
Also, if I were to develop an iOS application (a second client), would it be wise to use a different clientID and client secret from a security POV?
You can't - even if there was a way, I could still just inspect the payload on the wire to determine the values. See section 8.5 of the OAuth 2.0 for Native Apps
Secrets that are statically included as part of an app distributed to multiple users should not be treated as confidential secrets, as one user may inspect their copy and learn the shared secret. For this reason, and those stated in Section 5.3.1 of [RFC6819], it is NOT RECOMMENDED for authorization servers to require client authentication of public native apps clients using a shared secret, as this serves little value beyond client identification which is
already provided by the "client_id" request parameter.
Your client id/secret parameters are just providing the identity of application making the request, as such it is recommended you'd want to create a different client for your iOS application, both from a security isolation point of view + for any analytics you want to gather about use of your application (e.g. 'how many sign in attempts are you retrieving by client id?' etc)
However, a threat actor could reverse engineer your settings, take your client id + secret and then start hitting your token endpoint with a username/password combo to attempt to brute force your application. If an endpoint accepts these values and returns a success/failure code, this is a useful attack vector for someone trying to compromise your system.
The current recommended approach is to use the 'Authorization code flow'
The best current practice for authorizing users in native apps is to
perform the OAuth authorization request in an external user-agent (typically the browser), rather than an embedded user-agent (such as one implemented with web-views).
Previously it was common for native apps to use embedded
user-agents (commonly implemented with web-views) for OAuth
authorization requests. That approach has many drawbacks,
including the host app being able to copy user credentials and
cookies, and the user needing to authenticate from scratch in each
app. See Section 8.12 for a deeper analysis of using embedded
user-agents for OAuth."
Have a look at AppAuth for Android for more information,
Related
We are building a mobile application and its API server with architecture as in the picture below.
We have WSO2 as the API gateway in front of the Spring Boot API Server. We use WSO2 API Manager to restrict who can call the APIs. Only clients that have registered with our WSO2 and have the correct consumer key and secret can call an API through WSO2, by which the client first call to WSO2's token endpoint to exchange the consumer key and secret with an access token, then call the desired API with the access token in header Authorization: Bearer <access token>
We have a problem that we don't know how to keep the consumer secret since security audits prohibit us to store the secret in mobile app installer package.
There were some questions already asked such as
WSO2 API Manager - How does mobile app connect to API Manager?
WSO2 Api Manager OAuth2 DCR security in public native mobile app
But no answers correctly point to the problem. Most of them was mislead by the complexity of oauth2 flow.
To make the problem specific and clear, please assume that our mobile don't have users to login. The goal of this problem is to allow only trusted mobile application to call the API through WSO2.
Please help suggest if this is possible or not. Or we have no choice but to allow anyone to call the API. Or WSO2' consumer subscribtion feature is not designed to be used directly from mobile app at all?
After doing some research, I found 2 options people usually do.
Separate APIs into 2 groups. First group contains APIs that need to be used without user login, such as API to get initialization data or to get data for landing page of the app. Thease APIs are set as public, allow anyone to call without clientId and secret. The seconds group contains secured APIs that required the token. Mobile app can use Oauth2 PKCE flow to exchange the token with user identity proof.
Obfuscate clientId and secret and keep them in mobile app installer package. The APIs are still be separated into 2 groups as before. But the first group requires client-level token (oauth2 client credential type) and the second group requires user-level token (resource owner password or authorization code type)
I prefer option 2. In my opinion, I think the first option does not really make sense. People choosing this option, maybe, just do it to bypass the security audit check list, to not store the secret in public client, without really concern about security problem. It's like when you cannot trust your kids to keep a key to your house safely, so you decided to remove the lock from the door.
Having every APIs protected and keep the key in the client. Even though some hacker can manage to find the secret, he can only hack APIs of the first group and you can track the clientId he used. You know the expect behavior of the client so it is easy to setup an alarm that detect malicious activities from the client and revoke the token, reset the secret and rollout more complex obfuscation algorithm.
You may want to read OAuth 2.0 for Native Apps [RFC7636] spec. It states:
Public native app clients MUST implement the Proof Key for Code
Exchange (PKCE [RFC7636]) extension to OAuth, and authorization
servers MUST support PKCE for such clients, for the reasons detailed
in Section 8.1.
Check the below answer too.
How to implement Oauth2 without sending client_secret in WSO2 APIM
I created an Android app that uses AppAuth to authenticate with Google OAuth. In the Google Cloud Platform Console, I created an Android OAuth 2.0 client ID for my app and provided the app package name and signing-certificate fingerprint. Everything is working fine.
I wanted to verify that only my app can use that client ID. So I created a second app with a different package name and signed it with a different signing certificate. Using that same client ID, I'm still able to authenticate with Google and access APIs. I didn't think this should be the case. I was looking at the source code for AppAuth and it doesn't look like it ever uses the app signing or package name during the authentication flow. It uses PKCE of course, but I expected more to be happening.
So if I can steal my own client ID with little effort, what's to stop someone else from extracting my client ID from my APK and using it for authentication? The custom scheme I'm using for redirect URI is easy to figure out based on my package name. So the rouge app could configure AppAuth to use a similar redirect URI and capture the authorization result. And since PKCE is only used to verify the authorization request and code exchange come from the same place, a rouge app would be doing both so there's no real protection there either.
I could switch the client ID type to Web or Other, but that will require me to use a client secret, which is just as insecure if you embed it in an app.
Am I missing something or is Google OAuth working exactly as it was intended?
For client side Google OAuth 2, your Client ID does not really matter. The client performs the OAuth flow and the client receives the OAuth token. The magic is that the client must authorize Google. Anyone can steal your Client ID but they cannot do anything with it. As part of the OAuth lifecyle you should be validating OAuth tokens. Your backends should NOT be blindly accepting anything from a client - or anywhere not under your absolute control.
Your Client ID is not a secret and you can put that in the clear in your code.
It is the Client Secret which must remain secret. The Client Secret is not involved in client side authentication. The Client Secret is used on your backend servers.
I think that you are confusing the process. When the client application (your app, a web browser, etc.) authenticates with Google Accounts, your app is not being authorized. The client is being authorized. The client should use good judgement on which websites they visit (or apps) and use their Google logins with. The only thing that a client can do with their token is access their own data (Google Drive, Gmail, etc.). If your backend servers are accepting the client's OAuth token to manage access, then you are responsible for validating that token and its desired usage on your systems and who that token is authorized from.
A better choice is to perform authentication and authorization on the backend (your web server for example). Then you can implement the Google OAuth redirect to send the OAuth token to your servers. You are protected in that only authorized origins (your domain name for example) and authorized redirect URIs (an endpoint on your web server) can be involved in the authentication process. You then store the token in your client session, renew when necessary, add authorization scopes as required, etc.
I frequently use both methods (client side, server side) and both work well.
Thought I'd add a postscript here, related to the recommendations for mobile apps in Financial Grade APIs, to use Claimed HTTPS Schemes to receive login responses. This restricts use of the client id to apps with your digital signature.
For further info, see my blog posts, which describe code samples that anyone can run:
Android Code Sample
iOS Code Sample
Only native and one page apps don't have a client secret, so they have to switch to something else. Here the necessary protection is the redirect URL. For web hosted one page apps this is not a problem no matter how it is set up. They are hosted exactly on this redirect URL. For native apps another step has to be done. Android and iOS allow the use of "App-Claimed https URL Redirection". This ties an https URL directly to the app. These claimed URL redirections can be requested and must then be confirmed via signature on a specific address: https://developer.android.com/training/app-links/verify-site-associations.
With this method, in order to use a foreign client_id, the https redirect must be intercepted by the fake client (only rooted environment, which most phones are not).
With a lot of effort, a fraudster could run another frontend with the same client_id.
In a use case like this:
Using the API generates costs (e.g. own API)
Advertising on the frontend generates revenue
this is a reason to rethink the architecture.
In a use case described by you, this isn't a problem, as you don't own the API resource. The resource owner, which is the logged in GCP user, will have to pay for their GCP usage. On public clients, you cannot have the client itself authenticate with the authorization server (only the user is authenticated).
I am developing an Android app that integrates with users OneDrive for Personal account and obtains a Files.Read scope.
I have registered the app on Microsoft Application Registration Portal. Got a Client ID for authenticating my app with OneDrive and I am using Microsoft Graph APIs.
Now I have to include that Client ID in my app which would be distributed through Google Play.
How safe is that? If anyone can obtain that Client ID, is it possible for them to temper with the user's data by sending fake requests using that Client ID?
Please note that this concern is specifically for Client ID to authenticate requests to OneDrive.
Also, do these answers holds good here ?
Answer 1: https://stackoverflow.com/a/37945932/1363471
Just a remark: the client ID is not a secret by design, so actually
there is no need to protect it.
See section 2.2 in RFC 6749 ("The OAuth 2.0 Authorization Framework"):
The client identifier is not a secret; it is exposed to the resource
owner and MUST NOT be used alone for client authentication.
Answer 2: https://stackoverflow.com/a/14565249/1363471
I know this won't be a good StackOverflow answer, but I don't feel
able to explain it better than the Threat Model and Security
Considerations (RFC 6819). So here is the paragraph about obtaining a
Client Secret and its relative consequences.
Note that an Android app is a Public Client (a Native Application to
be more specific) so, as you say, unable to keep confidential its
credentials, but still able to protect Tokens and Authorization Code.
Answer #1 is correct, the Client ID is not a secret and you shouldn't be worried about packaging it inside your app. Its goal is to identify the client making the request, e.g. your app, not to authorize the call. The access token, which you get as the output of a successful OAuth flow and should be able to protect, is what's used to authorize the call.
I am writing a Google App Engine application along with a CLI client, an Android client and a Javascript client. The purpose of this application is to allow one to control an Android phone on which the Android client is installed, by using either the CLI or the Javascript client.
Authentication plays a crucial role as a bug might allow a malicious user to control others' phones.
The Android client is written and works. It authenticates the user using this method. Basically, this give me a so-called SACSID token I can store in a cookie. When this cookie is set App Engine recognizes my user. The server application can then call the UserService to get a User on which to call getUserId(). getUserId() returns me an opaque string that identifies my user. So far so good.
Now I am writing the JS and CLI clients.
Because the CLI client has no reliable way of displaying a CAPTCHA or opening a browser, I suppose that it needs to use the Device API ("Client login" is not an option). This API requires using OAuth 2 for authentication. Also, I want the JS client to access the user's contacts, which also seems to indicates that OAuth 2 would be suitable.
If my user authenticates using OAuth 2, will I be hable to somehow transform this Google OAuth 2 token into the same opaque String that I get when the Android client connects ? If not, can I modifiy my Android Application so that it uses OAuth instead of a Sacsid token ?
To be more specific, I see three things that would solve my problem :
A way of getting an OAuth 2 token from the Account Manager
A way of exchanging the OAuth 2 token for a SACSID token
A way of getting the same opaque UserID both with the SACSID token and the OAuth2, but only if I can have both authentication system on the same application.
Something that seems similar to the third possible solution is to get the user's email address from both OAuth and the SACSID token, and to use that as the user ID. This however looks a bit clumsy to me :
Every time I receive an OAuth 2 request, I would need to call Google APIs to retrieve the user's email address (or build my own system of tokens which seems insecure and introduces many other difficulties).
The email address of a given user can change, making me lose the association between the user and his previous data.
Use End Points instead:
https://developers.google.com/appengine/docs/java/endpoints/
They use oauth2, they are easy to implement and have support for android IOS and Web.
We're currently in the process of implementing OAuth2 to secure our new API and not sure how to securely provide required functionality. We need to allow the following from a mobile device:
Immediately after downloading the app the user is able to take a picture and submit it without having to first log in.
While we want to allow anonymous user access, where a user does not need to log in or register to use certain functionality, we do not want to allow unauthenticated access to the API. This would normally be accomplished using the client credentials authorization flow to obtain and app access token, however this requires knowing the client secret. From what I've read, a mobile device is not considered a trusted client and should not contain the client secret, and hence should not be able to generate an app access token on its own.
We've come up with a few options to accomplish this requirement, but would like some input on them:
Embed the client secret in the app. Doesn't seem ideal from a security standpoint, but maybe we're missing an obvious way to secure it? We're targeting at least iOS and Android.
Generate an app access token offline and embed that in the app. Still not very secure, but at least the secret isn't exposed.
Allow access to certain functionality using only the client ID instead of an access token. This may be the simplest, but it introduces an inconsistency and requires multiple ways of authenticating the client.
Build and use a companion web app to generate app access tokens for the mobile app. On the surface seems like a winner, but now you have to secure access to the companion app!
How would you securely authenticate access to an API using OAuth2 from a mobile device without requiring the user to first log in?
Agree with the comments on the Q. Either:
1.) Use Client Credentials grant type in OAuth 2 - with an embedded secret in your App. Understand that this isn't super secure and someone will reverse engineer it eventually. Ideally each client would get a unique secret - so you could revoke a client if they're abusing its use.
2.) Live with that API being open - thereby not requiring an OAuth 2 access token at all. Maybe that API would be known only to your app - but again, it would only be a matter of time before someone reverse engineers it.
My group is having a similar discussion. Users can get the app and browse a catalog without having to sign-in. The catalog and other data is accessed via an API and we would like to force users to have an access_token for all calls.
Our current thinking is to
Always force the App to exchange a common clientId/secret for an access_token. So the app would get an access_token even for anonymous users. This would be via the client_credentials oAuth flow.
If the user signs in, use the oAuth password flow. They would pass in clientId, secret, username, and password. We would additionally allow them to pass in their anonymous token so that we could transfer any history from their anonymous session.
So for example...
access_token = api.oAuth.client_credentials(clientId, secret)
catalog = api.getCatalog(access_token)
authenticated_access_token = api.oAuth.password(clientId, secret, username, password, access_token)