I have read below article and it is just awesome. Everything from that article is clear however I have one major doubt.
https://stormpath.com/blog/the-ultimate-guide-to-mobile-api-security
The article author said that in 'OAuth2 password grant' while logging into the mobile application, just need to send email and password in order to get the access token from the API server, but I have read at many places that you also need to send client_id and client_secret in that request. I'm going to build my API using Laravel:
https://laravel.com/docs/master/passport#password-grant-tokens
Here you can see it forces me to send client_id and client_secret in that request.
I'm really confused about this. If I have to send client_id and client_secret in that request, first I need to get it from the authorization server by creating a client on it. So at which event, I should create that client? When a user tries to log in from the mobile application? I just need to know the exact flow.
Any help would be appreciated.
Thanks
A client gets created for the developers who need to integrate with the OAuth2 server. It has nothing to do with the specific users' login flow.
ex. I want to integrate with Facebook login; I create a client on Facebook and incorporate that into my service, its Facebooks way of knowing who my service is.
So, a user logs in through your application; your application then sends that username and password to a backend server. The backend server then adds the client_id and secret so the OAuth server can verify the authenticity of the request.
So in your case, a user logs into your mobile application, you send that login request (username and password, with SSL) to your backend server. Your backend server then forwards that request to the OAuth2 service looking like the request below.
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'user#email.com',
'password' => 'user-password',
'scope' => '',
],
This directly returns an access_token and a refresh token that you can safely store in your mobile application.
I create the grant client in a migration called ConfigurePassport and set the key i want the app to use. You do not need a client per user.
public function up()
{
/*
* This command will create the encryption keys needed to generate secure access tokens.
* In addition, the command will create "personal access" and "password grant"
* clients which will be used to generate access tokens
*/
Artisan::call( 'passport:install', array('-n' => true) );
// Set Password Grant Client secret to known key
DB::table( 'oauth_clients' )->where( 'password_client', 1 )->update(
['secret' => env( 'GRANT_CLIENT_SECRET', 'dfhsdfhbtg545fdf45yedh5f5blahblah' )]
);
}
The above migration runs the artisan command passport:install as per the documentation to install the client. https://laravel.com/docs/master/passport#password-grant-tokens
Now your mobile app can request a token like so: the unique per user params are username and password.
You can find the client id in the oauth_clients table where password_client is true. It will likely be 2.
$http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'dfhsdfhbtg545fdf45yedh5f5blahblah',
'username' => 'taylor#laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
There are two different concepts:
Client: is the piece of software that's intended to communicate with your server. Usually, you will have 3 main clients which are your iOS, Android and web apps.
User: Which is the end-user that will interact with one of your clients, then the client will be communicating with the Oauth server on behalf.
So you will need to generate a client_id & client_secrete only one time. Then you can use these keys to be the authorized middle-man between your Oauth Server & the end-users.
In the case of Password Grant, The client_key & secrete_key are used to obtain the access_token for every user of each client you have.
Once the client obtains the access_token of a particular user (usually upon logging-in), the client will not need to send the client_key & secrete_key anymore, only the access_token.
However, if that user's access_token has expired, you will have to use those keys to exchange a new access_token with the refresh_token you already received from the login process.
Related
How do I combine Passport authentication and normal laravel authentication?
I want the user to be logged in on pages of web-middleware and api-middleware. The login route is in api-middleware. Currently I have set up Passport authentication and it works fine for all api-middleware routes. How to make the user logged in in web-middleware as well?
Edit #1
What Im doing:
Login code
$http = new \GuzzleHttp\Client();
try {
$response = $http->post(config('services.passport.login_endpoint'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('services.passport.client_id'),
'client_secret' => config('services.passport.client_secret'),
'username' => $args['email'],
'password' => $args['password']
]
]);
$user = User::where('email', $args['email'])->first();
Auth::guard('web')->login($user);
return [
"token" => $response->getBody()->getContents(),
"user" => $user
];
} // ...
Somewhere in some web-middleware route
return auth()->check() ? "logged in" : "not logged in";
returns "not logged in"
Ideally you shouldn't, as passport auth is for a separate app communicating to the API and laravel preshipped auth is for MVC, they are separate user sessions.
But assuming you know what you are doing, either call Auth::login($user); on user login via API, or generate the passport token when the user login through web middleware auth, whichever login happens first...
Remember Auth::login($user); creates a user session and sets cookies to refer to that session... So you create for yourself a new problem were on logout, there are two places to logout from... as technically the user is logged in twice, with passport token and with a cookie referring to his session...
Actually I'm in a situation like you were. I have searched a lot about it. I always needed web authentication because of nature of my projects but in addition I started to develop projects with api backend soo late in comparing with web development world.
I'm a bit lazy so I generally use Laravel Passport and without working always out of the box, it does the job so in my opinion if you want just the functionality of access tokens for api security, put your user login authentication on web side and just authenticate the api endpoints with auth:api middleware in your api.php file.
I know that that's not the best practice but since it sounds that you are not developing a pure Laravel SPA then you can follow the route for Laravel Multipage application with Vue powered.
But believe me best way is to use either web authentication or api authentication not together then as the above answer says, you will have two authentication working at the same time which does not sound ok.
At the end, when you want to get the authenticated user on blade templates you will use
auth()->user()
but on the other hand in api controllers you should use
auth('api')->user()
which is nice to have but dangerouse to use.
If you need to log an existing user instance into your application, you may call the login method with the user instance.
Auth::login($user);
You can also use the guard() method:
Auth::guard('web')->login($user);
See the documentation here for more information: https://laravel.com/docs/5.8/authentication#authenticating-users
I have setup Passport in Laravel 5 and am creating a token when a user registers by using this..
$user->createToken('My Token')->accessToken;
When I create a new user then I can see an entry in the oauth_access_tokens table that looks like this, I have shortened the id for display...
id | user_id | client_id | name | scopes | revoked
-----------------------------------------------------------
765765 | 193 | 1 | My Token | [] | 0
The API route I want to allow access to is protected, now I am trying to allow access by passing the following headers in Postman...
Authorization | Bearer 765765
Accept | application/json
But this is returning a response of unauthenticated, where am I going wrong?
Laravel Passport uses oAuth2. It's not as simple as generating a user token and being able to use it to authenticate. oAuth2 requires another step, which is called a token exchange.
You will have seen the oAuth2 process in action when you log into a website with Facebook. You click the login with Facebook button and you are sent to Facebook and you are presented with a dialog where you're asked to confirm or deny an app access to your account (Usually, specific parts of your account, a.k.a scopes).
That website will have it's own client account with Facebook and will have its own client ID and client secret. When you click that button, the website sends you to Facebook in order to gain your permission and an authorization code from Facebook. The website passes its client ID, requested permissions (scopes), a randomly generated session state (So it can verify later) and a URL to redirect to Facebook where you are shown the dialog.
When you accept, Facebook generates what is called an authorization code and sends you back on your way to the website (The redirect URL specified) along with the sessions state (So the website is able to verify the request) and the authorization code.
The website, on its back end will then ask Facebook to exchange your authorization code for an access token and will provide its client ID and client secret so Facebook is able to verify its authenticity. Facebook then responds with an access token and an expiry time.
Now, the website is able to access your account using the access token to be able to grab the requested information (Such as your email address for login).
It's also possible to do skip a lot of this process and not require the user to have to follow the whole redirection flow. To do this, (In Passport at least), you will need a password grant client. This is usually what you would do if you are using oAuth2 to authenticate an API.
The process here would be to generate a password grant client:
php artisan passport:client --password
In your database, the you will find in the oauth_clients table, a password grant client with a client ID and secret. You would need to give this to whoever is consuming your API (Such as a mobile/cellphone app).
Now when your user wants to log in, the consumer of your API (In this case Postman) would need to provide the user's credentials (username/password) as well as the client ID and secret for your password grant client. It's also necessary to tell Passport that you want to authorize via password grant.
The example given in the docs looks like this:
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor#laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
When successful, Passport will return a 200 response and will return some JSON containing an access token and a refresh token. You use the access token to access the user's account and you use the refresh token to get a new access token (without requiring the user to log in again).
It is this access token that you need to provide as the Bearer in your Authorization header.
I am trying to integrate Uber with my web app following their oauth guides. I currently have a redirect from my website to my server, where I make a request to https://login.uber.com/oauth/v2/authorize and get a 302. However, I am not ever getting a response back from uber with the access_token and refresh_token.
I have a server running at https://mobilius-app.herokuapp.com and a front-end at http://mobilius-website.herokuapp.com. I am currently sending a request to https://login.uber.com/oauth/v2/authorize with payload of
{
'client_secret' => UBER_CLIENT_SECRET,
'client_id' => UBER_CLIENT_ID,
'grant_type' => 'authorization_code',
'redirect_uri' => `https://mobilius-app.herokuapp.com/api/uber_tokens`,
'code' => [the auth code]
}
https://mobilius-app.herokuapp.com/api/uber_tokens never gets a response from uber with the auth token and refresh token, however. Any help is much appreciated!
First you should double check if you set the correct redirect url in the dashboard.
Second your request to 'https://login.uber.com/oauth/v2/authorize' seems to contain invalid payload. Please follow the steps of the authentication guide. Briefly, you need to:
Send user to authorize url
Receive the redirect with a authorization code
make a POST call to: https://login.uber.com/oauth/v2/token
Store access and refresh token for future use
so my mobile app would be the client, i generated a client_id and a client_secret for it.
users who uses the mobile app have to login using their username/password.
Where should i store the client_id and client_secret? is it ok to expose them and simply place them hardcoded in the app?
It is definitely not the secure way of hardcoding them and just placing them in an app.
Actually its not that straight forward. I assume you created the client from artisan or from the pre-built Vue components. In either case there is more that you have to do in order so safely consume the oauth2 api without exposing any potential security vulnerabilities in your app.
Assuming your mobile users would register from the mobile, you would need to create user and oAuth2 client from your mobile API that you will expose for your clients( mobile apps ) to consume. For this you have to do the following:
After installing laravel passport perform the following artisan command
php artisan migrate
This will create the necessary tables to store oauth clients, their tokens and other related important information at db level. After this you would need to change client_id data type to VARCHAR(255) so as to store username as client_id instead of storing numeric client_ids.
Now go to your models and create a model for oauth_clients table so that you can create client pragmatically from the code while creating users.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class oAuthClient extends Model
{
protected $table = 'oauth_clients';
}
This will create a model class for you through which you can store oauth clients in the db while registering them in your app.
Route::post('/register-user', function () {
$email= \Illuminate\Support\Facades\Input::get('email');
$password=\Illuminate\Support\Facades\Input::get('password');
$user = new \App\User(array(
'name' =>\Illuminate\Support\Facades\Input::get('name'),
'email' => \Illuminate\Support\Facades\Input::get('email'),
'password' => bcrypt(\Illuminate\Support\Facades\Input::get('password')),
));
$user->save();
$oauth_client=new \App\oAuthClient();
$oauth_client->user_id=$user->id;
$oauth_client->id=$email;
$oauth_client->name=$user->name;
$oauth_client->secret=base64_encode(hash_hmac('sha256',$password, 'secret', true));
$oauth_client->password_client=1;
$oauth_client->personal_access_client=0;
$oauth_client->redirect='';
$oauth_client->revoked=0;
$oauth_client->save();
return [
'message' => 'user successfully created.'
];
});
This will generate an entry in user table and oauth_clients table which will be used by laravel passport to generate respective access_tokens for the user.In the above code snippet you have to note that to generate the oauth_client secret you have to use some strong formula of encryption that you feel comfortable using it with your application. Also use the same technique to generate the secret key on your mobile app for the respective client/user.
Now you can use the standard POST API offered by laravel passport to request access token through password grant using "oauth/token" using the following parameters:
grant_type : 'password'
client_id : '<email with which the user is registered>'
client_secret : '<generate the client secret from the mobile app>'
username : '<email with which the user is registered>'
password : '<password entered by the user>'
scope : '<leave empty as default>'
5.The above will give you a response, if everything is correct, similar to :
{
"token_type": "Bearer",
"expires_in": 3155673600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjMwZmM0MDk1NWY5YjUwNDViOTUzNDlmZjc2M2ExNDUxOTAxZjc5YTA5YjE4OWM1MjEzOTJlZmNiMDgwOWQzMzQwM2ExZWI4ZmMyODQ1MTE3In0.eyJhdWQiOiJzaHVqYWhtQGdtYWlsLmNvbSIsImp0aSI6IjMwZmM0MDk1NWY5YjUwNDViOTUzNDlmZjc2M2ExNDUxOTAxZjc5YTA5YjE4OWM1MjEzOTJlZmNiMDgwOWQzMzQwM2ExZWI4ZmMyODQ1MTE3IiwiaWF0IjoxNDc4MTQ1NjMyLCJuYmYiOjE0NzgxNDU2MzIsImV4cCI6NDYzMzgxOTIzMiwic3ViIjoiMSIsInNjb3BlcyI6W119.dj3g9b2AdPCK-im5uab-01SP71S7AR96R0FQTKKoaZV7M5ID1pSXDlmZw96o5Bd_Xsy0nUqFsPNRQsLvYaOuHZsP8v9mOVirBXLIBvPcBc6lDRdNXvRidNqeh4JHhJu9a5VzNlJPm3joBYSco4wYzNHs2BPSxXuuD3o63nKRHhuUHB-HwjVxj2GDwzEYXdZmf2ZXOGRJ99DlWGDvWx8xQgMQtd1E9Xk_Rs6Iu8tycjBpKBaC24AKxMI6T8DpelnFmUbMcz-pRsgCWCF_hxv6FpXav3jr1CLhhT58_udBvXjQAXEbtHeB7W_oaMcaqezHdAeOWDcnqREZHsnXHtKt0JpymcTWBkS2cg7sJzy6P9mOGgQ8B4gb8wt44_kHTeWnokk4yPFRZojkHLVZb8YL6hZxLlzgV1jCHUxXoHNe1VKlHArdlV8LAts9pqARZkyBRfwQ8oiTL-2m16FQ_qGg-9vI0Suv7d6_W126afI3LxqDBi8AyqpQzZX1FWmuJLV0QiNM0nzTyokzz7w1ilJP2PxIeUzMRlVaJyA395zq2HjbFEenCkd7bAmTGrgEkyWM6XEq1P7qIC_Ne_pLNAV6DLXUpg9bUWEHhHPXIDYKHS-c3N9fPDt8UVvGI8n0rPMieTN92NsYZ_6OqLNpcm6TrhMNZ9eg5EC0IPySrrv62jE",
"refresh_token": "BbwRuDnVfm7tRQk7qSYByFbQKK+shYPDinYA9+q5c/ovIE1xETyWitvq6PU8AHnI5FWb06Nl2BVoBwCHCUmFaeRXQQgYY/i5vIDEQ/TJYFLVPRHDc7CKILF0kMakWKDk7wJdl5J6k5mN38th4pAAZOubiRoZ+2npLC7OSZd5Mq8LCBayzqtyy/QA5MY9ywCgb1PErzrGQhzB3mNhKj7U51ZnYT3nS5nCH7iJkCjaKvd/Hwsx2M6pXnpY45xlDVeTOjZxxaOF/e0+VT2FP2+TZMDRfrSMLBEkpbyX0M/VxunriRJPXTUvl3PW0sVOEa3J7+fbce0XWAKz7PNs3+hcdzD2Av2VHYF7/bJwcDCO77ky0G4JlHjqC0HnnGP2UWI5qR+tCSBga7+M1P3ESjcTCV6G6H+7f8SOSv9FECcJ8J5WUrU+EHrZ95bDtPc9scE4P3OEQaYchlC9GHk2ZoGo5oMJI6YACuRfbGQJNBjdjxvLIrAMrB6DNGDMbH6UZodkpZgQjGVuoCWgFEfLqegHbp34CjwL5ZFJGohV+E87KxedXE6aEseywyjmGLGZwAekjsjNwuxqD2QMb05sg9VkiUPMsvn45K9iCLS5clEKOTwkd+JuWw2IU80pA24aXN64RvOJX5VKMN6CPluJVLdjHeFL55SB7nlDjp15WhoMU1A="
}
You can use these token safely from your client apps ( mobile apps ).
Hope it helps!.
i am using https://github.com/lucadegasperi/oauth2-server-laravel in laravel framework for creating Api's. i configured all setup. when i make a call to get token it shows
{ "error": "invalid_request", "error_description": "The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the "grant_type" parameter." }
My grant type is -
'grant_types' => [ 'client_credentials' => [ 'class' => '\League\OAuth2\Server\Grant\ClientCredentialsGrant', 'access_token_ttl' => 3600 ] ],
token is -
'token_type' => 'League\OAuth2\Server\TokenType\Bearer', i need sample url for access my api.. whether i need to pass access token where can i get access token.
i dont find any tut about geting access token and use them correctly. please help me on this..
thanks in advance.
I am using the same package with laravel 5. There is this tutorial that explains it well.
After you publish the package settings, run migrations and add some test data in the oauth_clients and your users tables. Specifically you add client_id and client_secret in the oauth_clients table. Also create a user with username and password in your users table. Finally using POSTMAN chrome extension provide the following data to a POST route to get your access_token.
grant_type as password,
client_id as what you added above,
client_secret as what you just created above,
username your test username,
password your test user password,
You should get correct access_token from POSTMAN.
I think what you need is to include your client_id and secret_id parameters
that you got from github when you created your OAuth application. I use socialite and I had used the following information inside config/services.php
'github' => [
'client_id' => 'CLIENT_ID',
'client_secret' => 'CLIENT_SECRET',
'redirect' => 'REDIRECT_RUL'
]
Of course that's a temporary solution, if you use laravel 5 I suggest you to save this information inside .env files as they are more secure.
More about .env more about socialite