Microsoft Graph in a LARAVEL PHP app - oauth2 grant method 'password' - laravel

i'am using a LARAVEL PHP application which is trying to get a token from Microsoft GRAPH API.
Acutally, i can easily get a token by using the an OAuth2 Authorisation CODE grant method.
here is the way to do it : https://github.com/microsoftgraph/microsoft-graph-docs/blob/master/concepts/php.md
But using this method is redirecting the browser to a Microsoft authentication web page, and i don't want this. I would like a transparent method without any browser redirection.
Here are all the Oauth2 grant méthods to get a token :
https://alexbilbie.com/guide-to-oauth-2-grants
So i want to get a token by using "Resource owner credentials grant". Here is my code :
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => 'b9e6844b-b20c-4eef-bbff-fa052c1c0f94',
'clientSecret' => '1A8yUUcMaD5Zdv1dgwqy3SR',
'redirectUri' => 'https://my-website-url/oauth',
'urlAuthorize' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
'urlAccessToken' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
'urlResourceOwnerDetails' => '',
'scopes' => 'openid email profile' // Admin restricted scopes : Directory.Read Directory.ReadWrite Groups.Read.All
]);
$accessToken = $provider->getAccessToken('password',[
'username' => 'myaccount#assystem.com',
'password' => 'xxxxx'
]);
But i got the following error :
(1/1) IdentityProviderException
invalid_grant
in GenericProvider.php (line 217)
Any help?

After getting an error exception, i know why i got this error :
try {
// Try to get an access token using the resource owner password credentials grant.
$accessToken = $provider->getAccessToken('password', [
'username' => 'myapp#assystem.com',
'password' => 'mypassword'
]);
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
// Failed to get the access token
throw $e;
}
'error_description' => 'AADSTS70000: The grant is not supported by this API version ...(Microsoft Graph API)

Related

Authorization code grant with Socialite setup

I'm trying to implement an Authorization code grant with Socialite. I'm able successfully to receive users in the callback, but struggling to set up the other part of auth. I've created the personal passport client and received the client ID & Secret in oauth_clients. Whenever I use the createToken() I am able to see oauth_access_tokens these tokens with the name of PKCE(which contains the same name as in oauth_client). The $code is taken from the query in the callback, however, the $response in the very end is null, any idea why?
public function callbackGoogle()
{
// Receive the user credentials from social
$googleUser = Socialite::driver('google')->stateless()->user();
// Get the user by email
$user = User::where('email', $googleUser->email)->first();
// Create token
$user->createToken('PKCE')->accessToken;
// Get query parameter code from callback
$code = request()->query()['code'];
// Get client data
$client = DB::table('oauth_clients')->where('name', 'PKCE')->first();
$response = Http::asForm()->post('http://localhost:5000/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => $client->id,
'client_secret' => $client->secret,
'code' => urldecode($code),
'redirect_uri' => $client->redirect,
]);
// // Receiving null
dd($response->json());
}
You are trying to exchange an access token from google with your own application.
This does not make sense.
With this line:
$googleUser = Socialite::driver('google')->stateless()->user();
You have already exchanged the code from the URL with a token from google.
If you want to get an API token for your API, you already did this with
$user->createToken('PKCE')->accessToken;
You could use
$token = $user->createToken('PKCE')->accessToken;

Why am I getting an Unauthorized response with the Outlook API with Azure Active Directory?

I'm creating a task that will retrieve the messages in outlook mail with Microsoft Azure Active Directory.
I setup my azure account. Register an app, add certificate then add user to my AD. My signin method returns an access token which means signin is successful and pass the access token to outlook messages API. but the Outlook messages API returns unauthorize.
Here is my scope: email Group.Read.All Mail.Read Mail.Read.Shared Mail.ReadBasic openid profile User.Read User.ReadBasic.All Mail.ReadWrite
I used Laravel HTTP Client to send request. Hope anyone can help me, Im stuck on this problem for week
public function __construct()
{
$this->params = [
'client_id' => env('OAUTH_APP_ID'),
'scope' => env('OAUTH_SCOPES'),
'client_secret' => env('OAUTH_APP_PASSWORD'),
'username' => 'xxxxxxxx#mytenant.onmicrosoft.com',
'password' => 'xxxxxxxx',
'grant_type' => 'password',
'redirectUri' => env('OAUTH_REDIRECT_URI'),
'urlAuthorize' => env('OAUTH_AUTHORITY').env('OAUTH_AUTHORIZE_ENDPOINT'),
'urlAccessToken' => env('OAUTH_AUTHORITY').env('OAUTH_TOKEN_ENDPOINT'),
'urlResourceOwnerDetails' => '',
];
}
public function signin()
{
$url = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/token';
$response = Http::asForm()->post($url, $this->params);
if($response->ok()){
$returnData = $response->json();
$mail_api = 'https://outlook.office.com/api/v2.0/me/messages';
$messagesResponse = Http::withToken($returnData['access_token'])->get($mail_api);
dd($messagesResponse);
}
}
Here is the response of my signin. I used Laravel HTTP client to send request.
And for additinal info in my granted permission
It has mentioned in the document. One of the following permissions is required to call this API:
https://outlook.office.com/mail.read
wl.imap
And the permissions in your issue like Mail.Read, Mail.Read.Shared, Mail.ReadBasic are used for Microsoft Graph API, such as Get message API.

401 Unauthorized when creating Password Grant Tokens with laravel passport

I have created an api with passport and want to use password grant tokens because it is more of application to application interactions. But i get below error when i try to access it.
GuzzleHttp\Exception\ClientException
Client error: `POST http://127.0.0.1:8000/oauth/token` resulted in a `401 Unauthorized` response: {"error":"invalid_client","error_description":"Client authentication failed","message":"Client authentication failed"}
my route
Route::get('/get_token_by_password', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://192.168.0.103:8000/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 13,
'client_secret' => 'f37AwQGsVMiZDVu786KiRdbpn4MXYSBWCvqNcqiC',
'username' => 'developer#ymail.com',
'password' => '123456789',
'scope' => '*',
],
]);
return json_decode((string) $response->getBody(), true);
})->name('get_token_by_password');
What might I be doing wrong?
Also, how do I use postman to get the token?
How do i get a link to be used in other applications that want to consume my api
The request seems correct. Check if you have correct keys and client. After, check in DB the new value of client_secret.
php artisan passport:keys
php artisan passport:client --password
Your client id and/or client secret is not correct, since the error is "invalid_client".

Passport can not generate refresh token

Can you please help me ?
I want to generate refresh token in passport.
I can generate a refresh token only if I use default hash password from Laravel. What I mean is this situation: I create a new registration (from registration form) and try to call the api in Postman with both email and password.
$http = new Client();
$response = $http->post('http://localhost/passport/public/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => '2',
'client_secret' => '**************',
'username' => $request->email,
'password' => $request->password,
'scope' => ''
],
]);
But the problem is that I don't use hash password which laravel has by
default. I am using another hash password and when I call the API
http://localhost/passport/public/oauth/token in postman it shows this
error :
Client error: `POST http://localhost/passport/public/oauth/token` resulted in a `401 Unauthorized` response: {"error":"invalid_credentials","error_description":"The user credentials were incorrect.","message":"The user credential
You should create a new hashing driver as a standalone class by extending the Illuminate\Hashing\AbstractHasher class and implementing Illuminate\Contracts\Hashing\Hasher interface.
After that you have to register your custom driver in the HashManager class. To do so, in the register method of a service provider put:
// Include the class at the top of your service provider
use Illuminate\Hashing\HashManager;
// Then in the register method
HashManager::extend('driverName', function ($app) {
// Replace the namespace and class with the one you have created in the first step
// Or resolve it from the app container
return new \App\Your\Custom\Hash\Driver();
});
Once you have done that you can set the same driverName you have chosen when you extended the HashManager in the config/hashing.php file, by setting the driver attribute.
In this way you replaced the default bcrypt hashing systemwide and the credential matching should work without any further modification.

Laravel - Oauth password grant

I find it difficult to wrap my head around the password grant authentication process.
What I'm trying to achieve: A mobile device sends the username and the password of a registered user to the API and gets the access token as a response in JSON form.
What I've achieved so far: The API receives the user credentials (username and password) and authenticates the user using Laravel's built-in Auth authentication system.
I know that at this point I should proceed to Oauth authentication. Once the credentials pass the Auth step, I have the user's username, password (specified in the users table) and I can get the client_id and client_secret from the oauth_clients table, but I'm not sure how to accomplish the rest. Do I need to make another POST request? If yes, then how do I do it from the controller?
I am actually implementing this right now. Let me show you some code.
This is my part of my login function:
// this does the process for getting the access token
$oauth = AuthorizationServer::performAccessTokenFlow();
// some hacks
$oauth = (array) $oauth;
// more hacks
$oauth = json_decode($oauth["\0*\0data"], true);
// checks if a token was actually generated
if(!in_array('bearer', $oauth))
{
// returns what was generated if the token is missing
return Responser::error(400, $oauth);
}
You would have to post additional data on your login other than the username and password of the user.
So your post request will contain:
username=the_username
password=the_password
grant_type=password
client_id=the_client_id
client_secret=the_client_secret
note that grant_type=password is constant.
Now you have to check the configurations of the package too found at app/config/packages/lucadegasperi/oauth2-server-laravel/oauth2.php:
You should have the following code:
'password' => array(
'class' => 'League\OAuth2\Server\Grant\Password',
'access_token_ttl' => 604800,
'callback' => function($username, $password){
$credentials = array(
// change this to username if username is the field on your database
'email' => $username,
'password' => $password,
);
$valid = Auth::validate($credentials);
if (!$valid) {
return false;
}
return Auth::getProvider()->retrieveByCredentials($credentials)->id;
}
),
And you are done.
update
the code above that generates the token is inside this function:
// allow user to login with username or email and password
$user_pass = array('username' => $username, 'password' => $password);
$email_pass = array('email' => $username, 'password' => $password);
// check if input is email and use $email_pass combination or else use $user_pass combination
$login = ($isEmail->passes() ? $email_pass : $user_pass);
// try to authenticate username & password
if (Auth::attempt($login))
{
// now you are authenticated here
// get the client id and secret from the database
// maybe use curl or some hacks
// login stuff and token generation
}
This is a bit of an old post, but if you are looking to do this without having multiple post requests, you could always just do something like:
//Get Oauth creds
$apiCreds = OauthClient::where('email', Input::get('email'))->first();
//Provide additional post data for password grant
Input::merge([
'grant_type' => 'password',
'client_id' => $apiCreds->id,
'client_secret' => $apiCreds->secret,
'username' => Input::get('email'),
'password' => Input::get('password'),
'scope' => 'all'
]);
//Login
return AuthorizationServer::performAccessTokenFlow();

Resources