That question is hardly related to AppLinks assetlinks.json appears not to be used for validation
I am implementing Oauth2 apps on Android. I would like to do SSO (single sign-on) and I have a concern about AppLink to secure the Autorization Code.
The native app, through the browser, initiate an Authorization Request and then receive an Authorization Response containing the Authorization Code. According to RFC6749#section-4.1.2, the code is passed inside the URL:
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
Authorization Code is a sensitive piece of information because it allows a client to fetch an Access Token and Refresh Token and then access to a protected ressource.
To protect that code, the native app must implement https scheme redirects (RFC8252#section-7.2 & section-8.1). On Android, that must be done using the assetlinks.json file
But according to the related question linked at the top, app links on Android seems not to be 100% secure, because the OS may not verify the https scheme.
In that context, how are we supposed to implement Oauth2 authorization code hook ?
EDIT
According to #benjamin anwser, AppLink is not for security. But, the related threat use-case is the following : a malicious app is installed and uses the "SSO Cookie" to get the Auth Code and exchange it for AT+RT. It seems to me that nothing in the process can prevent that case: if applink is not for security, how does the Authorization Server can be aware that this app is a malicious app ?
Note: By SSO-Cookie, I mean the use of CustomTab to do SSO on Android.
It's not the AppLinks functionality that guarantees security. When you configure the /.well-known/assetlinks.json file it allows a transparent redirect to the application without any user interaction. This means that the modal dialog that usually appears to chose which application the user would like use to handle the link won't pop.
As you stated, this mechanism can be bypassed if the user chooses to handle your link with another app or your app is not yet install. The user needs to go to the phone setting in order to achieve this :
Settings > Apps > choose the application which can handle the Authorization Code link > Open by default > Open supported links in this app > choose Always allowThis way a third party application can catch the information contained in your link, in your case the Authorization Code.
That been said, it's actually the Proof Key for Code Exchange (PKCE) that guarantees security of Authorization Code. Your server must implement this feature in order to mitigate unwanted Authorization Code replays.
A little reminder on the OAuth authorization steps using PKCE :
The client (application) calls the /authorization endpoint with a hashed random string and the method which was used to hash it. The client keeps the random string (not hashed) because it will used in the step 3.
The authorization server checks if user entered a right login/password. If all it's good, it stores the hashed random string and it's hash method which were sent in the request. The server then redirect the user agent to the application with a redirection containing the Authorization Code.
In order to retrieve it's Access Token, the client (application) calls the /token endpoint with random string generated in step 1.
The server receives the /token request, extracts the random string and hashes it with the method stored in step 2. Then the server must checks that this hashed string matches the one stored in step 2. If the strings are the same, the server responds with an Access Token and a Refresh Token, otherwise with an error.
This is how the PKCE ensures that the client that made the /authorization request it's the same client that will make the /token request. So even if a third party app captures your Authorization Code it can't use it to retrieve Acess Tokens.
For more informations see : PKCE (rfc 7636) and OAuth 2.0 for Native Apps (rfc 8252)
So, Here is what I understand about how Google Oauth2 works.
Every application or a website (client) needs to register its project and get a client_id and a client secret.
The client_id and a redirect uri are used by the client to make an authorization request to the auth url on behalf of the user who is logged in.
When this happens, there is a pop-up and the user(resource owner) is prompted to allow or deny access to the user's protected resource for the client.
If the user accepts then the user is redirected to the redirect uri from where the client or the application can get the authorization code.
This authorization code is further exchanged for an access token from the OAUTH URL. This access token is later used in the Api calls the client make.
The problem is with the step 3. I am not running a web app or a website. Rather an application that can make curl function calls. Step 4 and step 5 are doable but, How do I bypass the step 3 as I don't have browser capabilities? Is it even possible?
Also can someone please tell how an android app does it? Because even the android app shouldn't have an inbuilt browser. Thanks in advance
We have the OAuth2 for Devices flow to take care of the problem you are trying to solve See this
The user can authorize the app from a different device that has a web broswer.
Every application or a website (client) needs to register its project and get a client_id and a client secret.
Correction / clarification: Any application that wishes to access private Google data must first be registered on Google Developer console. If the data to be accessed is public then a API key can be used. If the data is private then either Oauth2 credentials must be created or service account credentials.
Oauth2 credentials allow a user to grant an application access to a portion of their data (Denoted by scope) the application is identified by the client id and client secret.
Service accounts would allow for preauthorized access to private data normally owned by the developer working on the application. They do not popup the request for user authentication. Please see my article on this Google Developer console service account I don't want to go into service accounts here as you appear to be concerned with Oauth2.
2 . The client_id and a redirect uri are used by the client to make an authorization request to the auth url on behalf of the user who is logged in.
Correction / clarification: The first step in the Oauth2 dance is to request access from the user. This is done via a web page.
https://accounts.google.com/o/oauth2/auth?client_id={clientid}.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/analytics.readonly&response_type=code
Redirect uri is used to tell the authentication server where to return the authorization code to. In the event this is a web page application it would be the full location to a webpage capable of handling the next steps in the process. As you can see I have not put a web page in. This is in sense localhost. It tells the authentication server to just return the code to where ever it was I just sent my request from. It is used in windows applications and probably android although I am not an android programmer so I am not sure its just an educated guess.
4.If the user accepts then the user is redirected to the redirect uri from where the client or the application can get the authorization code.
Correction / clarification: Its probably your code that's redirecting you. I don't think its the server redirecting you but I may be wrong. The authentication server can send the code were ever you want it would be up to the developer to redirect the user someplace after the exchange.
5.This authorization code is further exchanged for an access token from the OAUTH URL. This access token is later used in the Api calls the client make.
Correction / clarification: access token is only good for an hour and you might also get a refresh token back which can be used to get a new access token.
Addressing your problems
The problem is with the step 3. I am not running a web app or a website. Rather an application that can make curl function calls. Step 4 and step 5 are doable but, How do I bypass the step 3 as I don't have browser capabilities? Is it even possible?
This is going to depend upon which api you are going for and whos data this is. If this is data owned by your users then request access from them save the refresh token and then when you need to access it again in your curl script you can just get a new access token and you have access. Its only the initial authorization you need to bother your users with.
Second option if this is your data that you personally have access to you may be able to user a service account. Service accounts are like dummy users you can preauthorize their access. I could create a service account add it as a user on a folder in my google drive and it would then be able to read and write to my google drive with out that popup window.
Also can someone please tell how an android app does it? Because even the android app shouldn't have an inbuilt browser. Thanks in advance
I am not an android developer I think part of it is magic in the Android SDK the credentials you get for android apps are even different. Cant really help with this one.
I would like to create an app in which there is a mobile (Android) client which uses REST API from the server. A user has to login with Facebook account (using Facebook SDK's LoginButton); on success this should create a user account on the server at the first log in.
I've already read a lot of tutorials about how to secure HTTP API using SSL and access tokens, but there is one point which I don't get. The flow should look like this:
a user log in on the Android app with the Facebook LoginButton
in the Android app I receive an access token on successful log in which I can push to the server
I can validate this access token against Graph API
if validation in 3. is succesful I can create a user account on the server
all other calls to my server API can be secured with received access token or other token which would be created by me
but what about the 2. point? I have to expose API call which takes an access token and creates an account. This API call won't be secured, so if someone calls it with stolen/properly fabricated access token, then I will create an account which shouldn't exist. How to solve this? Do I have to assume that if my create account API is called with an access token which is valid (because I validate it in 3.) then everything is ok? Is there a better solution?
You are right, never trust the client. Always validate all client input again on the server.
In your case, you're validation of the token on the server in Step 3 should include comparing the result from Graph API with the result from decrypting the user info from the token. If both match, then proceed.
There are several code examples on Facebook website on how to do this correctly. They are available in several server languages (e.g. PHP) so I recommend reviewing them.
My Android app needs to send an authorization code to my server so that the server can use that to acquire an access token for the user's Google Drive account. I have been trying to figure out how to acquire the authorization code and I found this in the Google API documentation (Using OAuth 2.0 for Installed Applications):
This sequence starts by redirecting a browser (system browser or
embedded in the application as a web view) to a Google URL with a set
of query parameters that indicate the type of Google API access the
application requires. Like other scenarios, Google handles the user
authentication and consent, but the result of the sequence is an
authorization code. The authorization code is returned in the title
bar of the browser or as a query string parameter (depends on the
parameters sent in the request).
After receiving the authorization code, the application can exchange
the code for an access token and a refresh token. The application
presents its client_id and client_secret (obtained during application
registration) and the authorization code during this exchange. Upon
receipt of the refresh token, the application should store it for
future use. The access token gives your application access to a Google
API.
Now I am not sure how to get this authorization code in my Android app since the Android examples I have seen seem to get the access tokens directly. I am looking at the Android AccountManager class and it has a method getAuthToken but this seems to refer to the access token and not the authorization code.
So how does one acquire the authorization code that can be shared with a server? If it is possible I would greatly appreciate some example code. If this is not possible what are the possible workarounds?
You may want to take a look at the Cross-client Identity document. It should keep you from needing to pass user tokens back and forth.
I believe you can actually take the access token returned by the Android AccountManager, send this to your server, then have your server make a call against the Google Drive API using that same access token - it is a bearer token and not bound to the channel that created it, so please take good care of it and only send over encrypted connections.
Documentation on how to get that access token can be found here:
https://developers.google.com/drive/quickstart-android
While that access token is good for immediate use, it will expire in less than 1 hour, so if you are looking for a solution that enables your backend server to have continued access to the Drive data, without the user being present at your app at the time of request, an alternate approach will be needed.
I'm developing a website that is primarily accessed via an app, and I want to use OAuth2 for user registration and authentication. Since it is an Android app I will start using Google's OAuth2 stuff, since it provides a decent UI on Android.
Google states that "You can choose to use Google's authentication system as a way to outsource user authentication for your application. This can remove the need to create, maintain, and secure a username and password store." which is what I want to do. However when I go through all their examples and whatnot, I can only find stuff about having a website or an app authenticate a user against Google's services.
And indeed, when I go to register my app ("client") with Google's OAuth2 there are options for website clients and "installed" clients (i.e. a mobile app) but not both. I can create two separate clients but I read the OAuth2 draft and I think there will be a problem, which I will now explain.
Here's how I did envisage it working:
User asks MyApp to access his private data.
App uses Android's AccountManager class to request an access token for Google's APIs.
Android says to user "The app 'MyApp' wants access to your Basic Information on Google. Is this ok?"
User says yes.
AccountManager connects to Google's OAuth2 server using the credentials stored on the phone, and asks for an access token.
Access token (which follows the green lines) is returned.
AccountManager returns the access token to MyApp.
MyApp sends a request to MySite for the user's private data, including the access token.
MySite needs to verify the user, using the access token. It validates the token as described here, with Google - "Google, is this token valid?".
Now, what I want to happen is that Google says "Yes, whoever gave it to you is indeed that user.", but what I think will actually happen (based on the OAuth2 draft and Google's documentation) is that it will say "No way! That token is only valid for MyApp, and you're MySite. GTFO!".
So how should I do this? And PLEASE don't say "Use OpenID" or "Don't use OAuth2" or other similarly unhelpful answers. Oh and I would really like to keep using the nice AccountManager UI rather than crappy popup WebViews
Edit
Provisional answer (I will report back if it works!) from Nikolay is that it should actually work, and Google's servers won't care where the access token came from. Seems a bit insecure to me, but I will see if it works!
Update
I implemented this pattern with Facebook instead of Google and it totally works. The OAuth2 server doesn't care where the access token comes from. At least Facebook's doesn't, so I assume Google's doesn't either.
In light of that it is a very very bad idea to store access tokens! But we also don't want to have to hit Facebook/Google's servers to check authentication for every request since it will slow everything down. Probably the best thing is to add an additional authentication cookie for your site that you hand out when their access token is validated, but a simpler way is just to treat the access token like a password and store a hash of it. You don't need to salt it either since access tokens are really really long. So the steps above become something like:
9. MySite needs to verify the user, using the access token. First it checks its cache of hashed valid access tokens. If the hash of the token is found there it knows the user is authenticated. Otherwise it checks with Google as described here, with Google - "Google, is this token valid?".
10. If Google says the access token is invalid, we tell the user to GTFO. Otherwise Google says "Yes that is a valid user" and we then check our registered user database. If that Google username (or Facebook id if using Facebook) is not found we can create a new user. Then we cache the hashed value of the access token.
I just posted an answer to a similar StackOverflow question.
Google calls this Hybrid Apps and explains how an "Android app obtains offline access for Web back-end".
The gist of it is that you'll have to pass a massaged scope string into GoogleAuthUtil.getToken in order to get it to return an Authorization Code (not an OAuth2 Token). That Authorization Code can be passed from your mobile app to your server and be exchanged for an OAuth2 Token and Refresh Token, according to this schematic.
The scope parameter needs to look something like this:
oauth2:server:client_id:<your_server_client_it>:api_scope:<scope_url_1> <scope_url_2> ...
You can use the access token retrieved by the mobile application anywhere else. Drive SDK has a nice and simple intro that goes through the flow on https://developers.google.com/drive/quickstart-android
it describes exactly what you want:
https://developers.google.com/identity/protocols/CrossClientAuth
You probably need OpenID Connect, which uses OAuth tokens for authentication. As for AccountManager, the current OAuth support is a bit hacky, the new Google Play Services, set to be released 'soon' should hopefully make this better. See here for a demo.
At least with Google, the access token eventually expires. This is why the android AccountManager has the invalidateAuthToken method--the cached access token has expired, and you need to tell the AccountManager to stop giving you the old one and instead get a new one. This makes it somewhat safer to cache the token, as the token itself doesn't give you eternal access as that user. Instead, when valid, it merely says "at some point in the recent past, this token was acquired by a trusted source."
Here are a couple of things I've found helpful when working with tokens. The first is Google's tokeninfo endpoint. The token itself is just base64-encoded JSON. This means it isn't encrypted, so you need to be sure to be using HTTPS for communication. However, it also means that you can examine the token and have a better idea of what's going on.
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=
If your token was "abcdef", you would navigate to:
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=abcdef
and Google would unpack the token for you. It is a simple JSON object that includes an "expires_in" field telling you the number of seconds for which the token is still valid. At 6:03 in the video below you can see the unpacked token:
https://developers.google.com/events/io/sessions/383266187
That video includes a thorough overview of OAuth2 and is well worth watching in its entirety if you're going to be dealing with OAuth and tokens. The speaker also discusses other forms of Oauth2 tokens, that are not access tokens, that do not expire.
Another useful resource is the OAuth Playground. This lets you do basic things like request scopes, make up requests, and get back tokens. This link seems to work sporadically, and on Chrome I had to install the Oauth Playground app:
https://developers.google.com/oauthplayground/
And here is a tutorial by Tim Bray, the speaker in the video, explaining how to use access tokens to communicate to a server from an Android app. This was useful to me because I began to understand how the different things in the Google API Console work together:
http://android-developers.blogspot.in/2013/01/verifying-back-end-calls-from-android.html
With regards to the actual answer to your question, I would say you never need to cache the access token on the server. As explained in the Verifying Back End Calls from Android link above, verifying a token is almost always a fast static call, meaning there's no reason to cache the tokens:
The libraries can cache the Google certs and only refresh them when required, so the verification is (almost always) a fast static call.
Finally, you can indeed use the AccountManager to get access tokens. However, Google now instead encourages the use of the GoogleAuthUtil class in the Play Services library instead:
In a nutshell what's the difference from using OAuth2 request getAuthToken and getToken
Here note the comment by Tim Bray, the same guy yet again from the above links, saying that they are putting their efforts into the GoogleAuthUtil route. Note, however, that this means you would be limited to Google authentication. I believe that the AccountManager could be used to get, for example, a Facebook token instead--not the case with GoogleAuthUtil.
When we had a need to do something similar on a non-google OAuth Server, we kept the tokens in a DB on the website. The app would then use web services to request the token when needed to request data.
The user could do the OAuth registration on either the web or app. They shared the same application token, so they could share the same access token. After the registration we would store the access and refresh tokens in the DB for use from whichever app needed it.