So, I’m developing an API using slim/slim
and league/oauth2-server
to manage the OAuth2 connection. OAuth2 will be useful because I will need to use Client Credentials grant between services.
Then, I’m also developing an hybrid app with React Native. This app will requires user login by using e-mail and password or connecting with another services (such as Facebook, Google, Twitter, etc).
And I’m confused about what OAuth2 flow to use for this case. Across the web are a lot of articles saying that Resource Owner Password Credentials is not safe anymore, and we should use instead Authentication Code with PKCE.
But I can’t discover or understand how to apply Authentication Code with PKCE in a first party app, because all documentation talks about you will need the uses a browser to get authentication code in redirect_uri
.
The flow I imagine is something like that:
- User open the app, then insert your credentials
username
andpassword
; - This screen will connect to API
/request_token
URI sending{ 'grant_type': 'password', 'username': username, 'password': password, 'client_id': CLIENT_ID }
, considering it as a public app we can’t sendclient_secret
; - The API validates credentials and returns some data such as
{ "access_token": access_token, "token_type": "JWT", "expires_in": LIFE_SPAN }
, here we will use JWT to gerenate theaccess_token
based inpublic/private key
; - Authentication done, the app will store the
access_token
while it’s alive and when it expires will do the flow torefresh_token
.
My question: is it safe? Scott Brady did some “aggressive” article talking it’s NEVER safe.
How apps does this things? When I use Instagram app, for example, they own the app and the API, I don’t need a browser in the User Experience flow. Are modern apps using “Resource Owner Password Credentials” or “Authentication Code with PKCE”? There is a away to avoid insert browser in the flow while using “Authentication Code with PKCE”?
[EDIT] Possible SolutionAs Gary Archer said “Auth Code flow with PKCE is recommended – along with logging on via the system browser”, but we are not talking about grant permissions to access users data or third-party apps.
As a designer I don’t agree that loggin in the first-party app owned by the same API owner requires a browser this is the not the User Experience we are looking for. And all apps we see such as Instagram, Facebook, Uber… we just put your username and password and we have access to your account.
What I will do is create a custom version of Authentication Code with PKCE removing the required_uri
.
After a lot of search, I found some answers I think was interesting to adapt. As above, I removed redirect_url
from flow. Look:
- The flow starts in a login screen, when user give your credentials;
-
Client generates a
code_verifier
then hashescode_verifier
tocode_challenge
and sends it to Authorization Server with following parameters:response_type=code
: indicates that your server expects to receive an authorization code.client_id=xxxx
: the client id.client_integrity=xxxx
: app integrity check for first-party app.code_challenge=xxxx
: the code challenge generated as previously described.code_challenge_method=S256
: either plain or S256, depending on whether the challenge is the plain verifier string or the SHA256 hash of the string. If this parameter is omitted, the server will assume plain.username=xxxx
: username to authenticate.password=xxxx
: hashed version of password.state=xxxx
: a random string generated by your application (CSRF protection).
-
Authorization Server will validates user authentication, stores
code_challenge
and return theauthorization_code
with aclient_token
; -
After receive the
aauthorization_code
andclient_token
, Client saves theclient_token
and immediately sendauthorization_code
back to Authorization Server with following parameters:grant_type=authorization_code
: ndicates the grant type of this token request.code=xxxx
: the client will send the authorization code it obtained.client_id=xxxx
: the client id.code_verifier=xxxx
: the code verifier for the PKCE request, that the client originally generated before the authorization request.
-
Authorization Server will validates all data and, if everything is right, will return the
access_token
; - Client will set Authorization header with the
access_token
and always sendclient_token
to every request, it will be only accepted with both values are right; - If
access_token
expires, then Client will do a request to refreshaccess_token
and get a new one.
Now, I will reproduce this logic to PHP language. If everything goes right and I hope it does, I will be back with definitive answer.
[EDIT] ClarificationsI’m using OAuth2 to user connect with your third-party accounts (Google, Facebook, etc). But user also can log to a local account in my database. For this case, user doesn’t need to grant anything at all. So, no makes sense send user to a browser to him does your login.
I wondering if, to this case, local accounts, we can use Resource Owner Password Credentials or it’s more safe Authentication Code with PKCE (we already conclude it’s a better approuch). But Authentication Code with PKCE requires redirect_uri
, do I need uses this redirection to log users into a local account where they don’t need to grant access?
3
Answers
Let's go then. After a lot research, I found some approaches that I will apply and may work correctly. So, first of all, here is the challenges:
To prevent a malicious thing from arbitrarily using user credentials there are access tokens. They replace passwords and needed to be refreshed in short amount of time. That's why they are so much better than HTTP Basic Authentication.
That's why is recommended to use in modern apps the Authentication Code with PKCE, it provides all features and benefits of using OAuth2 protocol. But, here cames a long discussion and, even, problem for developer community:
To get an Authentication Code some user needs to make your login in a browser, grant access, redirect back to client and, soon, client will receive a code to exchange for an access token.
This scenario works good and NEEDS to be used for third-party apps. But, what if it is a first-party app? When you own the database with user data and you own the "trusted" app, redirect user doesn't make any sense. Right?
At this moment, my question is: how can I use the AuthCode (PKCE) flow without redirect user? And, again, it's important to highlight that talking about OAuth2 protocol is always the same that "to grant client to access resource server" (authorization, not authentication).
So the real question is: why Authorization Code needs a redirection at all? Then, I came with the following answer:
This flow requires to know client credentials and user consensus to turn back an authorization code.
That's why I was wrong in my edits. There's no change needed in OAuth2 protocol (sorry me for think different). For this reason, what OAuth2 needs is a authorization mediator, above your layer. Thus, the authorization code not will turn back to client, but to authorization mediator that, finally, will return it to client. Makes sense?
How it gonna work? Well, will be need 4 different "cores":
And, then, security techniques we may consider:
Let's look at flow:
Part one: autheticating user and client;
{ email, mobile_number, hash ( password ), verification_method }
) to Authentication Server route/login
;/login-otp
including the verification method (such as{ otp, verification_method }
);{ hash ( shared_secret ) }
to be used soon.Part two: authorizing API access;
shared_secret
Client will stores securely at mobile app, then it will ask for a authorization code using PKCE calling/auth
with{ response_type, client_id, scope, state, code_challenge, code_challenge_method }
, Authorization Server will validate credentials and return an authorization code with no redirects;/token
, but it will need to send some extra data:{ payload: { grant_type, code, client_id, code_verifier }, timestamp, hash ( some_user_data + timestamp + shared_secret ) }
;client_id
,code
andcode_verifier
responding with an access token;access_token
will return to Authorization Mediator and, after, to client granting access to API resources.Part three: accessing resource server;
/api
containing theAuthorization
header and some extradata with{ timestamp, hash ( some_user_data + timestamp + shared_secret ) }
;shared_secret
hashes, call Resource Server validatingaccess_token
and return data.Part four: refreshing access token;
/refresh-token
containing theAuthorization
header and some extradata with{ payload: { grant_type, refresh_token, client_id, scope }, timestamp, hash ( some_user_data + timestamp + shared_secret ) }
;shared_secret
hashes, call Authorization Server and return a new fresh token access.A visual image for this flow:
I don't think it is a perfect strategy, but it replaces Resource Owner Password Credentials to Authentication Code with PKCE and gives some extra security techniques. It's way better then a single and simple authentication method, preserves the OAuth2 protocol and mantaein a lit bit more hard to compromise user data.
Some references and support:
How do popular apps authenticate user requests from their mobile app to their server?
Why does your mobile app need an API key?
Mobile API Security Techniques
Secure Yet Simple Authentication System for Mobile Applications: Shared Secret Based Hash Authentication
Auth Code flow with PKCE is recommended – along with logging on via the system browser. Also the AppAuth pattern is recommended.
https://curity.io/resources/develop/sso/sso-for-mobile-apps-with-openid-connect/
It is tricky and time consuming to implement though – so you need to think about it – sometimes using a cheaper option is good enough. Depends on the sensitivity of data being exposed.
If it helps here are some notes for an Android demo app of mine, which also focuses on usability – and links to a code sample you can run:
https://authguidance.com/2019/09/13/android-code-sample-overview/
First of all, do not invent a OAuth grant simply because you need to adopt it in your application. It will make tings complex to maintain.
In your scenario you need to provide social login (ex:- Login via Google, facebook). This of course a desired functionality one must support. But it doesn’t limit you from obtaining end user credentials through a custom registration process. There are many reasons for this, for example not everyone use social media or a Google account. And sometims people prefer to register than sharing user identifier of some other service (yes, this is the opposite end of social login).
So go ahead, provide social login. Store user identifiers when first login through external identity server (ex:- Google). But also, have a good old registration step with password and an email.