Is it possible to make oauth secure on iOS?
I am investigating OAuth 2.0 as a means to implement single sign-on + authorization for a "suite" of iOS apps. To explain my concerns, I'll simplify and use Facebook + a 3rd party app that uses Facebook for authentication (let's say Words/"Words with Friends").
For the purpose of example, I'll assume that Facebook registers to support scheme/protocol "facebook://" and that Words registers to support "words://"
I also make the assumption that it is not possible to secure the "client-secret" or the protocol in an iOS application because you can decompile the application. Any ways that I have come up with to secure this results in security by obscurity.
Another assumption is that there is no way to prevent two applications from registering to handle the same protocol. The behavior when two apps both register for the same protocol is indeterminate. (Although it appears that the first app to launch on the device gets registered while the second apps registration is ignored)
If I understand the workflow between Facebook (user-agent) and Words (client) on the iOS device:
User launches Words
user chooses to logon via Facebook credentials
Words invokes openUrl("facebook://") which contains, among other things an identifier for Words as an application and a redirect uri (i.e. "words://")
iOS launches Facebook application
User enteres credentials, which Facebook application validates against Facebook authorization server.
User prompted to authorize Words to access Facebook data (i.e. Words can access my friends list)
Facebook invokes callback uri provided by Words along with access token (i.e. words://access_token?token_here)
Words uses this token to access my friends list (i.e. protected resource data)
Assuming the above is correct, if I want to be malicious and access random people's friends list, I could create an application that also registers to handle the protocol "words://" and get it on the app store. If someone has my app and Words installed and my app is the one that successfully registered (i.e. launched on the device before Words), then:
Launch Words, choose to logon, launches Facebook
user authenticates / authorizes
Facebook attempts to redirect back to Words by invoking openUrl on the redirect url
My App (not Words) is launched
My App now has access to the auth code, which (with the secret learned by decompiling) can be exchanged for an access_token, with rights to access your friends list
I am hoping that my reasoning is flawed above or I would have to conclude (specifically) that Facebook iOS authentication for 3rd party apps is insecure.
More generically, is it possible to implement OAuth 2.0 (authorization/implicit grant workflows) securely on iOS application?
Google has come up with an experimental solution for this problem that they call OAuth 2.0 for Installed Applications.
The Google OAuth 2.0 endpoint supports applications that are installed on a device...it is assumed that these applications cannot keep secrets.
Essentially, the shared secret is treated as non-secret.
At the time of this writing, most OAuth 2.0 servers do not seem to support this experimental design.
This design introduces the risk that an attacker could create a new client that represents itself as your application to the authorization server (the attacker would need to obtain the client identifier as you describe in your question, or by following one of the techniques suggested here).
However, this risk seems to be mitigated by the fact that the resource owner (the user) would be unlikely to authorize the malicious application to take any action on protected resources, since he/she will know that the application is not, in fact, your application.
Related
For confidential clients, there are scopes assigned to clients and the logged in user has to consent to them. Since there is client secret involved in exchange of auth code for access token, no one can impersonate them and take advantage of their scopes.
But when it comes to pkce flow on a native app, if I had someone else's clientId (clientIds are not considered private information) which has a lot of scopes, I could just start the flow with with their clientId. What is stopping a hacker from using some reputed clientId in the PKCE flow and have access to all their scopes?
NATIVE CALLBACK METHODS
If you look at RFC8252, certain types of callback URL can be registered by more than one app, meaning only a client ID needs to be stolen in order to impersonate a real app, as you say.
This still requires a malicious app to trick the user to sign in before tokens can be retrieved. And of course each app should use only the scopes it needs, and prefer readonly ones. After that it depends on the type of native app.
MOBILE
A mobile app can use Claimed HTTPS Schemes via an https callback URL to overcome this. It is backed by App Links on Android or Universal Links on iOS. Even if a malicious app uses the client ID, it cannot receive the login response with the authorization code, because it will be received on a URL like this, and the mobile OS will only pass this to the app that has proved ownership of the domain via the deep linking rehistration process:
https://mobile.mycompany.com/callback?code=xxx
DESKTOP
For desktop apps there are gaps, since only Loopback and Private URI Scheme callback URLs can be used. It relies on users to avoid installing malicious apps. Eg only install apps from stores that require code signing, which also inform the user who the publisher is. If users install malicious apps then perhaps they have deeper problems.
ATTESTATION
A newer technique is to use a form of client authentication before authentication begins. For confidential clients, Pushed Authorization Requests are used, which uses the app's client credential, so this cannot be used by native clients by default.
Mobile apps could potentially provide proof of ownership of their Google / Apple signing keys during authentication, and there is a proposed standard around that.
I have a mobile app using Azure AD B2C which works fine. I can create an account, login, forgot password etc. That's all fine.
The mobile app stores data on the device.
The mobile app should also be able to push data to a remote database and trigger various functions via a .net core web api.
The mobile app should only be able to do that if it authorises itself successfully with the api using the credentials, or access tokens, it has from the login.
I assumed that this could be implemented easily (because, why couldn't it be?)
But now, after weeks of searching, hours of video-watching, 10's of aborted [test] projects, & 1000's of user accounts; I'm not sure whether the thing I want to do is a thing that can be done or should be done.
Thoughts?
Yes, this is of course possible :)
You can check the code samples for some examples of how to do it: https://learn.microsoft.com/en-us/azure/active-directory-b2c/code-samples.
Your mobile app can acquire an access token for itself.
You'll have to define in B2C that your app contains an API, and then define the app ID URI (an identifier for the API, not a URL).
You can then define scopes that API exposes.
These can be different permissions that applications calling the API can have.
In your mobile app you then acquire an access token using those scopes.
This token can then be passed to HTTP requests to your API, which the API has to authenticate.
Do ask if you want to know about something specific.
More of a theoretical question here - how can you get around using OAuth when you don't want to use it, but are using an API that requires it.
For example recently I was looking through the Bing Ads API and noticed they now require you to do OAuth as part of the process. Which makes sense if you're making an application that allows a user to control their Bing Ads account via your app. However, let's say you wanted all of your users to interact with one Bing Ads account.
Is it possible to hardcode all of the OAuth pieces in the background and just use the same authentication for every user to essentially send their stuff to the same Bing Ads account.
- If so, what sort of negative impacts would there be on that?
While it is simply not possible to get around using OAuth if the API requires it, OAuth can be used for more than just the "access delegated to client by current user" use case. As you suggest, if you want all users of your app to interact with your Bing account ("on your behalf" on OAuth speak), you can certainly do that with OAuth.
For an OAuth 2.0 implementation this would mean that you obtain an access token and preferably a refresh token in some way for your app, e.g. by you yourself going once through the Authorization Code flow (also, some services allow you to generate tokens in their web UI). Then you would "hard-configure" the token(s) in your app and use it/them to talk to the Bing API.
If a refresh token is included as well as an access token then your app can get a new access token in the backend whenever the old one expires without you (or your users) having to go through that initial flow again.
Be aware that this is not good practice for mobile apps, where you would have to distribute your app with the tokens embedded in the binary packages. Those tokens could easily be grabbed through hacking/scanning those binaries. But when the tokens are used in a backend service and never exposed in the front end, this is a perfectly valid scenario.
This is more of a general question but I hope it is still valid for SO.
So far I have learned, that in general, a mobile app (such as official Pinterest app) use the Password credential flow to let their users login and access the API directly. (let's just assume they use OAuth for this)
So they collect username and password, send it to their server and get a token in return which is used for subsequent requests.
Now a user did not want to register and created an account using e.g. Facebook as the authorization server. So my question is:
How is this flow implemented?
My educated guess:
User chooses "Login with Facebook" in mobile app
Facebook Login Page opens with return_uri = mobile app
Mobile app receives auth token
Mobile app uses client credentials and says the API: Use this token for user X
Is this correct?
First of all, apps should not use the Password Credentials Grant. The specification is rather clear about it:
In the traditional client-server authentication model, the client
requests an access-restricted resource (protected resource) on the
server by authenticating with the server using the resource owner's
credentials. In order to provide third-party applications access to
restricted resources, the resource owner shares its credentials with
the third party. This creates several problems and limitations
The specification then goes on describing those problems.
And about the Resource Owner Password Credentials Grant:
The authorization server should take special care when enabling this grant type and only allow it when other flows are not viable.
The entire purpose of OAuth 2.0, I to not have to use something like the Password Credentials Grant, where the user hands over their password to the application.
About your second question: what happens when a user does not want to register and create an account with your app, but wants to use e.g. Facebook for authentication?
Remember that both the Implicit Grant, as well as the Authorization Code Grant, work by using a browser control to authenticate the user. In that browser session with the Authorization Server, you are free to authenticate your user in any which way you want. Certainly, you can use your own user/password database, but you could also use other mechanisms, such as WS-Federation. In your case, it sounds like the user want to authenticate using Facebook.
Authenticating using Facebook is then not done by your client app, but by your Authorization Server. It typically does that by using the Facebook Authorization Code Grant, followed by a call to read the user's profile to obtain their Facebook user id, name, and so on.
If you do not want to build such an Authorization server yourself, you can use an existing one. Several companies offer login-as-a-service solutions, including the one I work for.
UPDATE: You asked several follow up questions in a comment below. I'll answer them briefly here:
First of all, the fact that some companies that use OAuth to secure their services allow for a Password Credentials Grant, does not imply that you should. In fact, there are probably more examples of companies that don't offer this possibility, than companies that do.
There are real trust issues, and real security risks with sharing your password with a device app. To start with, the app on the device is easier to hack than a server. Furthermore, if you give the app your password, presumably that app also needs to store it somewhere for future use. As a user, I just have to hope that that storage is safe form possible malware running on my machine. For more issues, see the introduction in the OAuth 2.0 specification mentioned above.
Secondly, all good Authorization Servers differentiate between First Party Clients and Third Party Clients. A First Party Client such as yours is controlled by the same company that controls the Authorization Server, and for such an app the Authorization Server does not ask for user permission to share data, since it makes no sense to talk about sharing data with yourself. That is why the web sites of these companies don't ask you whether you allow to share the data they hold on your behalf with them. They already have it, and there is no "sharing" going on.
Of course, you might argue that you have never seen any of these companies talking about this distinction between First Party Clients and Third Party Clients. But the reason they don't should be obvious: when you deal with them, you are always a Third Party App. They don't need to tell you that they treat themselves differently.
The mechanism I would choose in your scenario depends on the nature of the client app, and the nature of the services it accesses. What are your requirements?
Anyway, if the device the application is running on has a secure storage facility, such as Windows Phone 8.1, I would probably consider using the Authorization Code Grant without client credentials. That way, the user never has to log in again. If we're talking about a web site or a SPA, I would consider the Implicit Grant (where the "remember me" feature, if any, is offered by the Authorization Server). Again, the specification gives advantages and disadvantages of each grant type for several scenario's.
Currently we are not using OAuth with our apps but we are working on making the shift, we have direct login and capture the user/pass that was entered and store those. We then turn around and use the stored credentials for a feature that allows the user to open a record within Salesforce.com, we pass the user/pass in to the login endpoint along with a starting URL to the specific record, this works great and is a well liked feature as it is a simple SSO from the App to Salesforce.com where the user can see all data that may not be visible within the app.
Moving to OAuth 2.0 and using the standard webflow, we no longer can capture the user/pass, which is actually a good thing as far as security is concerned. We would however like to keep this functionality, is there anyway of SSO'ing into Salesforce.com by passing along one of the OAuth tokens or some kind of sesson id?
After reading more and thinking about what OAuth accomplishes I feel like this probably isn't possible being that the tokens obtained are meant to be used only with the API and not with the front end system. I hope that I am wrong though and there is a way to login to the front end using these tokens.
EDIT
Ok I am editing to hopefully make this more clear. Currently user's authenticate using the login() API method with their user/pass, we store this user/pass locally (not ideal). We then sync a subset of data that the users can access anytime within the app, being that it is a subset, we have a feature to "SSO" to the Salesforce.com front-end. This simply opens Salesforce.com in a web-view (UIWebView) using the URL https://ns8.salesforce.com/?pw=PASSWORD&un=username#example.com&startURL=/recordId. This will log us in to Salesforce.com and open the specified record.
Moving forward we want to use OAuth 2.0 with the web flow so that we aren't handling the user/pass and so that we do not have to deal with Security Tokens or opening specific IP ranges to allow login without a Security Token.
With that said, is there anyway to use the tokens/credentials received from the OAuth authentication to open Salesforce.com, automatically log the user in, and goto a specific record?
I may have mis-used "single sign on" before, but in a sense, this simulates an SSO from our App to Salesforce.com, in that our users can touch a single button within the app and be logged in to the Salesforce.com web interface.
When you request an OAuth token, you can specify what scope it has, options include api only (the original type of tokens), or other options which include the ability to use the token with the UI pages. (see the scope parameter detail in the help). One of the still missing peices is a way to bootstrap the UI with that token when all you can do is tell a browser/webview to goto a URL, but a widely used (but unsupported) way is via frontdoor.jsp, e.g. you'd open https://{instance}/secur/frontdoor.jsp?sid={the_Access_token}&retURL={optional_relative_url_to_open} remember to URLEncode the 2 values.
So I think you are saying your application uses the SFDC username and password to just authenticate to retrieve a record from SFDC to display in your app?
IF this is correct - which I think it is - then you could just use the standard Salesforce Single Sign On system to authenticate. There is a guide here which outlines the process of setting up a SAML SSO system with Pat Patterson writing an interesting feature on how the security system works here. He has also written a great blog post on DeveloperForce here about the nitty details of OAuth in general for Force.com and not just the SAML setup. I have used the OAuth system in an iPad app against SFDC and it works quickly and easily. I can't see why your system should be unable to use the protocol as you desire.
Direct access into Salesforce is a key benefit of SSO and definitely provided. I'm not sure where you read that SSO in Salesforce is API only. From the SSO PDF pbattisson linked for you:
With single sign-on, users only need to memorize a single password to
access both network resources or external applications and Salesforce.
When accessing Salesforce from inside the corporate network, users are
logged in seamlessly, without being prompted to enter a username or
password. When accessing Salesforce from outside the corporate
network, users' corporate network login works to log them in. With
fewer passwords to manage, system administrators receive fewer
requests to reset forgotten passwords.
OAuth 1 & 2 are both supported, though I'm a fan of OAuth 2 since 1 has really finicky additional steps involving the order of parameters and their encoding sequences. I recently wrote an Apex-Twitter integration and quickly realized it wasn't going to be as easy as Facebook, which uses OAuth 2.0.
But in your case it sounds like you just want to provide users with the ability to actually login to Salesforce and go to a specific landing page once authenticated. This is definitely doable.
Good luck!