An application I'm working on allows users to log into an OAuth-enabled backend. The application is therefore privy only to the authentication tokens and user metadata, not to the user's credentials.
Within the application, users can hit links that open up links in a browser. These resources are also protected by OAuth, and the token obtained during login to the native app is also relevant to the web.
I would like the user's credentials to flow from the native app to the web browser in the standard OAuth manner (by including it as an Authorization
header).
It seems that Android facilitates this through its shared credentials feature, but I cannot find an equivalent for iOS. I did find the shared web credentials feature, but that seems to require knowledge of the user's credentials.
How can I flow OAuth tokens from my native app through to web browsers that it opens?
Associated Domains and Shared Web Credentials don't seem to be a good approach here.
You have two options:
- Passing the OAuth Access Token as URL-QueryString-Param to the
WebBrowser.
https://x.y.z/?access_token=abc
You'll have to manipulate the embedded URLs and assure that your backend understands this.
Very common and easy approach. Many websites like Facebook and
Google are passing Access Tokens in the URL.
- If you're using In-App-Browsers (UIWebView, WKWebView), you can intercept the URL-Request and add the Authorization Header on your own. See this for UIWebView and this for WKWebView (which is little bit harder than UIWebView)
Technically, you could just include the token in the URI you pass to the browser.
But this would be insecure:
Injection of access tokens
An additional (and very dangerous) threat
occurs when clients accept access tokens from sources other than the
return call from the token endpoint. This can occur for a client that
uses the implicit flow (where the token is passed directly as a
parameter in the URL hash) and don't properly use the OAuth state
parameter. This issue can also occur if different parts of an
application pass the access token between components in order to
"share" access among them. This is problematic because it opens up a
place for access tokens to potentially be injected into an application
by an outside party (and potentially leak outside of the application).
If the client application does not validate the access token through
some mechanism, it has no way of differentiating between a valid token
and an attack token.
(Source: https://oauth.net/articles/authentication/)
It is also forbidden in the specification:
Access token credentials (as well as any confidential access token
attributes) MUST be kept confidential in transit and storage, and only
shared among the authorization server, the resource servers the access
token is valid for, and the client to whom the access token is issued.
(Source: https://tools.ietf.org/html/rfc6749#section-10.3)
So instead, you could try using an alternative OAuth flow, called "Authorization Code Flow", where instead of passing the token to the browser, the app passes a special code, which the browser then uses to obtain a token from the server.
However, your use case isn't exactly what this mechanism was created for, so I'm not sure using it to accomplish what you're after would be in line with the specification.
First you should use HTTPS protocol - google for: let's encrypt (in order to get a free SSL certificate)
The flow you should consider:
A user opens up your mobile app and is prompted for their username
or email and password.
You send a POST request from your mobile app to your API service
with the user’s username or email and password data included (OVER
SSL!).
You validate the user credentials, and create an access token for
the user that expires after a certain amount of time. (google for: api rate limiting)
You store this access token on the mobile device, treating it like
an API key which lets you access your API service.
- Once the access token expires and no longer works, you re-prompt the
user for their username or email and password.
On the server side you should use stuff like: fail2ban, iptables and make sure that the linux version that you use is up to date. (you should update/upgrade from time to time)
On the web application (api) you should validate and serialize all the data. Never send more data that is needed and never accept partial data from the client. On the api application you should do xss (cross site scripting)/csrf (cross site request forgery) prevention. Take a look at OWASP TOP 10 - https://www.owasp.org/index.php/Top_10_2013-Top_10 . You should also use security headers - https://www.dionach.com/blog/an-overview-of-http-security-headers on the web api.
Never trust user input.
Why not use built-in apple func & libraries ?
Take a look at shared web credential
To enable shared credentials in your app, add the com.apple.developer.associated-domains key to your app’s entitlements. You can add this entitlement using the target’s capabilities pane (see Figure 1).
or using iCloud Keychain.
This document concentrates on the use of Keychain Services to store and retrieve passwords. Read this document if your application needs to handle passwords for:
Multiple users—for example, an email or scheduling server that has to authenticate many users
Multiple servers—for example, a banking or insurance application, which might have to exchange information with more than one secure database server
A user who needs to enter passwords—for example, a web browser, which can use a keychain to store the passwords a user needs for multiple secure web sites
Hope it may help