thewebartisan7's avatar

Passport Password Grant Tokens

I have setup with success Passport with password grant tokens. As explained here https://laravel.com/docs/5.6/passport#password-grant-tokens I need to send client_id and client_secret for request a token.

However I am building API for a mobile app and I would like to avoid to store in mobile app the client_secret.

I would like to know how to do that, and what is best practice.

I don't find any good example how to handle this, but Passport docs said to use Implicit grant.

Thanks!

0 likes
13 replies
frankperez87's avatar

Using password grants allows you to have the user login from your mobile app using their email and password. Then a token would be returned that you would use for all API calls.

Doing it this way makes it so that you do not have to store any sensitive information within the apps code base.

Hope this helps.

thewebartisan7's avatar

Hi frank, thanks for your reply. Yes I understand how it work, with username and password.

I have already implement it and work fine.

My question is another.

For request a token you need to send also client_id and client_secret.

This:

$response = $http->post('http://your-app.com/oauth/token', [
    'form_params' => [
        'grant_type' => 'password',
        'client_id' => 'client-id',
        'client_secret' => 'client-secret',
        'username' => '[email protected]',
        'password' => 'my-password',
        'scope' => '',
    ],
]);

However, this mean that you need to store the client_secret on mobile app.

All the resources I read say that hardcoding the client_secret to the mobile app is a very bad choice as it can be easily looked up and that app can be "decompiled" and hacker will retrieve your client secret. Once they have it, they can create their own application and impersonate your app.

So I would like to know best practice to avoid this, and also how to implement this with Passport on Laravel.

Thanks!

frankperez87's avatar

I'm curious to know where these resources state the best place to store them is? Do you have an article that you're referencing?

The client id and token is just a way to let your API know that the app connecting is approved. It does not actually mean someone will have access unless they are also given the username and password.

I would personally use environment variables to store the id and token, which would be referenced during your deployment/build process and not stored in your development files.

I'm always curious about learning something else that might make the apps more secure though :)

thewebartisan7's avatar

I think that you don't get the point of my question. I am talking about client_secret and not client_id and token. I am not sure if you understand, so I am linking you the official explanation of what a client_secret is: https://tools.ietf.org/html/rfc6749#section-2.3.1

I would personally use environment variables to store the id and token, which would be referenced during your deployment/build process and not stored in your development files.

I am referring to client_secret that a mobile app should know before token request. A mobile app don't have access to environment variables.

I suggest to read this article if you want to know more about: https://esbenp.github.io/2017/03/19/modern-rest-api-laravel-part-4/

In this article it explain how it should be. But he don't explain how to do with passport, but he suggest to use a library that author of article has make.

In normal case where you can store in your application the client_secret without worrying that can be hacked would be below scenario:

The client logs in directly from authentication server
======================================================
       User credentials
       Client credentials
Client -----------------> Auth server
       <-----------------
       Access token
       Refresh token

The client requests resources from the API
======================================================
       Access token
Client -----------------> API
       <-----------------
       Some resource

But with mobile apps or javascript app the source code can be visible to hackers. So you can't store client_secret in application.

So that article suggest below scenario:


The client logs in using the API which uses the auth server
===========================================================
                                          Client credentials
       User credentials      User credentials
Client ----------------> API ------------------> Auth server
       <----------------     <------------------
       Access token          Access token
       Refresh token         Refresh token

The client requests resources from the API
======================================================
       Access token
Client -----------------> API
       <-----------------
       Some resource

I understand how it should work, but my question is how this can be done with Passport. I don't have experience with Laravel nor with Passport, so I am asking if someone has already had this similar issue, and maybe there is already a good solution that would save me time.

D9705996's avatar

My advice would be to create a before middleware that takes the Request parameters that you are happy to pass from the client application and merges with an array of the parameters you do not want to expose e.g. client_id and client_secret.

I would generate a new client_id and secret and hardcode these in the middleware and test if you can then post to the oauth/token route with just the username, password and grant_type ( you will need to add your middleware to this route only)

Assuming this works you can extend to fit your own use case, e.g. create a new client when user registers and associate with the Users model. Then in the middleware you can find the users specific id and secret in the database and replace the hardcoded values.

I am not sure this is best practice but meets your goal of hiding the client_* keys in the backend

thewebartisan7's avatar

My advice would be to create a before middleware that takes the Request parameters that you are happy to pass from the client application and merges with an array of the parameters you do not want to expose e.g. client_id and client_secret.

I got it, that is the first thing that I think.. I see that Passport it's so complete in every aspect, so I guess that exist something already to handle this out-of-the-box.

Assuming this works you can extend to fit your own use case, e.g. create a new client when user registers and associate with the Users model. Then in the middleware you can find the users specific id and secret in the database and replace the hardcoded values.

You don't need to create a client for each user. A client is an app, is not an user. Example a client is a mobile app, another client is desktop app, and another again is a tablet app.

As you can read on an article, see screenshot: http://esbenp.github.io/img/api-desktop-smartphone-tablet-clients.png

Quote: ". If your business is powered by your own API it becomes easier building and maintaining multiple clients (e.g. a desktop app, a smartphone app, tablet app etc.) since they all utilize the same business platform (the API). "

client_id and client_secret it's like the username/password of application itself, for allow authorization for use API. While username/password of user is for authentication.

D9705996's avatar

Previous comment was related to storing your unique business logic that specifies how the request data is populated within the middle-ware rather than a definitive method.

This is the reason you cannot find a package/tutorial that will give you some general code to use as everyone's use cases will be different.

thewebartisan7's avatar

I understand how to store; but the point of this question is another one.

You are saying to store client_id and client_secret and then retrieve it.. But is not the problem for server to know the right client_secret. The problem is how mobile app can know it, and where to keep..

But I have already some solution how to handle this; as seem there is not anything ready to use offered by laravel community.

Thanks for your time.

Zhythero's avatar

I know I'm late to the discussion but if anyone is wondering about this, this is currently an issue with passport's Password Grant. In the docs, it is stated there that this type of grant is used by first-party client side clients like Mobile App or SPAs. However, the token endpoint requires secret which makes it insecure.

As a workaround, I have altered the schema of oauth_clients table by making the secret NULLABLE. After creating the password grant client, I then set its secret key to NULL. This way, when requesting for access token, I don't have to provide secret to the endpoint.

You can view this issue on passport's repo here: https://github.com/laravel/passport/issues/984

thewebartisan7's avatar

@kenthisrael thanks for this info, I will take into account for my next project. I quickly read the issue that you mention, but not all since I have not time right now, there is also links to other issue in phpleague package. Seem not yet fully fixed this issue, even in new release. I start use passport instead of JSON Web Tokens for security reasons, even if passport is more complicated to setup. But considering this, most probably is better to stay with JWT.

Please or to participate in this conversation.