400 bad request : can't figure out the request problem - php

I have to read a file in a OneDrive sheet using prestashop.
I can't use the microsoft/microsoft-graph packet with composer because it need guzzle v6 and prestashop is locked at v5 so i'm rigting the request myself but it seems i'm missing something.
I got the credential figured out, I have my access token (without it or a random string gives me an 401 error).
Now I'm trying to read a file, but even easier than that, I can't list the recent files in my drive.
I can do it on the graph explorer (beeing connected) and do whatever I want but whtin the code I always get an "400 error Bad request" with no other detail.
Here is my code, if you can point me to te right direction?
$url = 'https://graph.microsoft.com/v1.0/me/drive/recent';
$req = $guzzle->get($url, [
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.3 Safari/533.2',
'Accept' => 'application/json, text/plain, */*',
'Authorization' => 'Bearer ' . $accessToken,
],
'body' => [
],
]);
EDIT :
The problem seems to be about authorisation, here is the code :
<?php
// 1/ retrieve the access token
$guzzle = new \GuzzleHttp\Client();
$url = 'https://login.microsoftonline.com/' . $tenantId . '/oauth2/token?api-version=1.0';
$token = json_decode($guzzle->post($url, [
'body' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'client_credentials',
//should I use this
'roles'=>[
"Sites.Read.All",
"Files.Read.All"
],
//or this?
'scp' => 'Sites.Read.All Files.Read.All'
],
])->getBody()->getContents());
$access_token = $token->access_token;
// 2/ read file content
$userId = 'email#domain.fr';
$url = 'https://graph.microsoft.com/v1.0/users/' . $userId . '/drive/items/##FILE_ID##/workbook/worksheets(\'Export\')/range(address=\'a2:d4\')?$select=values';
//$url = 'https://graph.microsoft.com/v1.0/users/' . $userId . '/drive/list';
$guzzle = new \GuzzleHttp\Client();
$req = $guzzle->get($url, [
'headers' => [
'Authorization' => 'Bearer ' . $accessToken,
]
]);
// this return a 403 forbidden
$ret = json_decode($req->getBody()->getContents());

Most likely you acquire access token (application permission token) via Client credentials grant flow, in that case there is no Me context since it represents signed-in user context (applicable for delegated permission token only).
For application permission token user needs to be explicitly specified, for example:
To call List recent files endpoint at least Files.Read.All and Sites.Read.All permission levels are required.
Once permissions are granted and consented as illustrated below
roles claim should contain the following list of permissions:
"roles": [
"Sites.Read.All",
"Files.Read.All"
]
$userId = "<username>#<tenant>.onmicrosoft.com";
$url = 'https://graph.microsoft.com/v1.0/users/' . $userId . '/drive/recent';
$guzzle = new Client();
$headers = ['Authorization'=>'Bearer ' . $accessToken];
$response = $guzzle->get($url,['headers' => $headers]);
$json = json_decode($response->getBody()->getContents(),true);
To determine whether access token is delegated or application permission, jwt.io service, for instance, could be utilized:
for application permission token, the permissions are provided in the roles claim:
For delegated permission token, the permissions are specified via scp claim instead
How to get access token
function getAccessToken($tenantId, $clientId, $clientSecret){
$guzzle = new Client();
$url = 'https://login.microsoftonline.com/' . $tenantId . '/oauth2/token?api-version=1.0';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'client_credentials'
],
])->getBody()->getContents());
return $token->access_token;
}

Related

microsoft graph:You cannot perform the requested operation, required scopes are missing in the token

I am calling one Microsoft graph API from my PHP application, API is https://graph.microsoft.com/beta/policies/identitySecurityDefaultsEnforcementPolicy
my code is like below
$graph = new Graph();
$graph->setAccessToken(session('my_token'));
try{
$response = $graph->createRequest("GET", "/policies/identitySecurityDefaultsEnforcementPolicy")->execute();
}
catch(Exception $e){
dd($e);
}
$arr = $response->getBody();
dd($arr);
but it always catches exception and displays the below error
Client error: `GET https://graph.microsoft.com/v1.0/policies/identitySecurityDefaultsEnforcementPolicy` resulted in a `403 Forbidden` response:
{"error":{"code":"AccessDenied","message":"You cannot perform the requested operation, required scopes are missing in the token.","innerError":{"date":"2022-11-23T06:47:39","request-id":"9a4573c7-fd72-44ae-8ac6-8e4589cf1497","client-request-id":"9a4573c7-fd72-44ae-8ac6-8e4589cf1497"}}}
all the other Microsoft graph APIs are working well
I have also given permission to Policy.Read.All and granted admin consent to the Microsoft app I am using here for auth.
Update: when I open Microsoft's online token parser https://jwt.ms/ and parsed my token, I see the roles like
"roles": [
"Mail.ReadWrite",
"User.ReadWrite.All",
"SecurityEvents.Read.All",
"Mail.ReadBasic.All",
"Group.Read.All",
"MailboxSettings.Read",
"Group.ReadWrite.All",
"SecurityEvents.ReadWrite.All",
"User.Invite.All",
"Directory.Read.All",
"User.Read.All",
"Domain.Read.All",
"GroupMember.Read.All",
"Mail.Read",
"User.Export.All",
"IdentityRiskyUser.Read.All",
"Mail.Send",
"User.ManageIdentities.All",
"MailboxSettings.ReadWrite",
"Organization.Read.All",
"GroupMember.ReadWrite.All",
"IdentityRiskEvent.Read.All",
"Mail.ReadBasic",
"Reports.Read.All"
]
but not the Policy.Read.All
Update: Getting auth token code is
$guzzle = new \GuzzleHttp\Client();
$url = 'https://login.microsoftonline.com/'.env("TANANT_ID").'/oauth2/token?api-version=beta';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => env("CLIENT_ID"),
'client_secret' => env("CLIENT_SECRET"),
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'client_credentials',
],
])->getBody()->getContents());
// echo $token->access_token;
Session::put('my_token', $token->access_token);
When you're requesting the token, you need to supply a scope URL,
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#get-a-token
So as a basic example (this might not give the permission you need) but shows what your missing.
$guzzle = new \GuzzleHttp\Client();
$url = 'https://login.microsoftonline.com/'.env("TANANT_ID").'/oauth2/token?api-version=beta';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => env("CLIENT_ID"),
'client_secret' => env("CLIENT_SECRET"),
'resource' => 'https://graph.microsoft.com/',
'scope' => 'https://graph.microsoft.com/.default',
'grant_type' => 'client_credentials',
],
])->getBody()->getContents());
// echo $token->access_token;
Session::put('my_token', $token->access_token);
specifically notice that i have added
'scope' => 'https://graph.microsoft.com/.default', to your form params
Looks like you don't have Policy.Read.All permission , could you please cross check permission through azure portal and provide the required permission and try again.
Thanks

Why does Microsoft Graph API deny my request to upload files to OneDrive from a PHP website?

I have a website where my clients can view their invoices and upload design assets related to the invoice. I want to create a PHP form which uploads the files to OneDrive, instead of storing them on our web server.
I have followed the instructions in this StackOverflow answer to get started.
I have created the app in Microsoft Azure, and entered the appropriate Application (client) ID, Object ID, and Directory (tenant) ID. I am using a client secret to authorize the application, and submitting a POST request using my account's email and password to get the access token.
The relevant code for my upload form looks like this:
if (isset($_FILES['uploads'])) {
$guzzle = new \GuzzleHttp\Client();
$tenantId = 'xxxxxx';
$clientId = 'xxxxxx';
$clientSecret = 'xxxxxx';
$url = 'https://login.microsoftonline.com/' . $tenantId . '/oauth2/token';
$user_token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'password',
'username' => 'xxxxxx#companydomain.com',
'password' => 'xxxxxx'
],
])->getBody()->getContents());
$user_accessToken = $user_token->access_token;
$graph = new Graph();
$graph->setAccessToken($user_accessToken);
foreach ($_FILES['uploads']['name'] as $key => $name) {
try {
$graph->createRequest(
"PUT", "/drive/root:/Documents/".$_FILES['uploads']['name'][$key].":/content"
)->upload(
$_FILES['uploads']['tmp_name'][$key]
);
} catch (Exception $e) {
var_dump($e);
}
}
}
This code throws an exception, showing the following error message when I try to upload a :
Client error: `PUT https://graph.microsoft.com/v1.0/drive/root:/Documents/Screen%20Shot%202022-10-30%20at%204.03.44%20PM.png:/content` resulted in a `403 Forbidden` response
In the Azure API Permissions, I have granted the Files.Read.All, Files.ReadWrite.All, and User.Read permissions. However, when I check the access token in https://jwt.ms/, I just see "scp": "User.Read".
So it looks like my access token might not have the correct permissions, but I can see that I do have permissions to read and write files when I look at the API Permissions page in Azure.
How can I further debug this issue and find a solution to upload files from my server to OneDrive?
Thanks to Nikolay's comment, I added a scope parameter to form_params and that fixed it.
I also changed the grant_type to client_credentials, and removed the username/password authorization.
$user_token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'client_credentials',
'scope' => 'https://graph.microsoft.com/.default'
],
])->getBody()->getContents());
$user_accessToken = $user_token->access_token;

GitLab oauth2 Laravel "{"message":"401 Unauthorized"}"

My gitLab controller. Links taken from the documentation. After submitting the form
returns an error "{"message":"401 Unauthorized"}" . Token is coming, but i want to
get username and email.
My gitLab controller
public function callback(Request $request)
{
$response = Http::withHeaders(['Accept' => 'application/json'])
->asForm()
->post('https://gitlab.com/oauth/token',[
'client_id' => config('oauth.gitlab.client_id'),
'client_secret' => config('oauth.gitlab.client_secret'),
'code' => $request->get('code'),
'grant_type' => 'authorization_code',
'redirect_uri' => config('oauth.gitlab.callback_uri'),
]);
$token = $response['access_token'];
$response = Http::withHeaders(['Authorization' => 'token ' . $token])
->get('https://gitlab.com/api/v4/user');
also link https://gitlab.com/api/v4/projects is work success
dd($response->body());
}
after checking I get an error 401. I don't understand why.
** My class GitlabServices**
public static function link(): string {
$params = [
'response_type' => 'code',
'client_id' => config('oauth.gitlab.client_id'),
'redirect_uri' => config('oauth.gitlab.callback_uri'),
'scope' => 'read_user openid'
];
return 'https://gitlab.com/oauth/authorize?' . http_build_query($params);
}
client_id, secret, redirect_uri store in .env
If you getting 401 in response. Check if the token privileges to request data.
Probably:
Token is not attached with request.
Token don't have privileges.
Adding 'token_type' to the request headers helped me
$token = $response->json('access_token');
$tokenType = $response->json('token_type');
$response = Http::withHeaders(['Authorization' => $tokenType . ' ' . $token])
->get('https://gitlab.com/api/v4/user');
The connection was successful and I received all the necessary information after making above mentioned changes.

Send a message to a Channel in Teams via Graph

I try to send a message to a specific channel in teams.
It works perfectly with 'https://developer.microsoft.com/de-de/graph/graph-explorer'.
For reciving the access token i do the following. This part works, because i can retrieve user information and other things with it.
$guzzle = new \GuzzleHttp\Client(['verify' => false]);
$tenantId = '<tenant_id>';
$clientId = '<client_id>';
$clientSecret = '<client_secret>';
$url = 'https://login.microsoftonline.com/' . $tenantId . '/oauth2/token?api-version=1.0';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'client_credentials',
],
'verify' => false
])->getBody()->getContents());
$accessToken = $token->access_token;
This part now is the one that produces the error
$graph = new Graph();
$graph->setAccessToken($accessToken);
$url = '/teams/<team_id>/channels/<channel_id>/messages';
$queryParams = [
'body' => [
'content' => 'foo'
]
];
$info = $graph->setApiVersion("beta")->createRequest("POST", $url)
->addHeaders(array("Content-Type" => "application/json"))
->attachBody($queryParams)
->setReturnType(BetaModel\User::class)
->execute();
echo '<pre>';
print_r($info);
This produces the following error
{
"error": {
"code": "UnknownError",
"message": "",
"innerError": {
"date": "2020-11-11T10:14:03",
"request-id": "<request_id>",
"client-request-id": "<client_request_id>"
}
}
}
The teams id and the channel id are correct, i can retrieve them by using
/users/<user_id>/joinedTeams
and
/teams/<teams_id>/channels
and as mentioned earlier, it works in the graph explorer page.
I know that i somehow need to send the message in an user context, but i can't make it work, and i can't find useful informations, so i am pleased for any. Thanks in advance!
This api call currently does not support application permissions, so you cannot use the client credential flow to obtain a token. You need to grant delegated permissions to the application, and then use the auth code flow to obtain the token. see:here.

How to setup O365 made possible login with thephpleague/oauth2-client and working with msgraph-sdk-php?

I try to implement a php client, access a mailbox of a user with graph api. Because it's a background service, which should fetching specific mails attachment, it must run with grant type password.
So far, I got it really easy working make a login with thephpleague/oauth2-client and grant type client_credentials:
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => '...',
'clientSecret' => '...',
'redirectUri' => 'https://login.microsoftonline.com/common/oauth2/nativeclient',
'urlAuthorize' => null,
'urlAccessToken' => 'https://login.microsoftonline.com/.../oauth2/token?api-version=1.0',
'urlResourceOwnerDetails' => 'https://graph.microsoft.com/v1.0/me',
]);
$accessToken = $provider->getAccessToken('client_credentials');
Now I try it with grant type password, but it fails:
$provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => '...',
'clientSecret' => '...',
'redirectUri' => 'https://login.microsoftonline.com/common/oauth2/nativeclient',
'urlAuthorize' => null,
'urlAccessToken' => 'https://login.microsoftonline.com/.../oauth2/token?api-version=1.0',
'urlResourceOwnerDetails' => 'https://graph.microsoft.com/v1.0/me',
]);
$accessToken = $provider->getAccessToken('password', [
'username' => '...',
'password' => '...',
]);
Response I get: invalid_request. Google that error, I come to https://learn.microsoft.com/de-de/azure/active-directory/develop/v2-oauth2-auth-code-flow which tells me, I should fix my request.
Now... somehow it's hard, finding a) a ressource telling which urls are right and b) finding a tutorial, tell how I add right a app so I can get it working (maybe also a result, because the o365 layout changes fast...)
Does anyone have a idea, what the right parameters are and what I must do in the Azure Active Directory Admin Center so its working?
My goal is "only", access a users mailbox and get attachments from existing mails. I think, when I can authenticate successfully, all other things are easy (hope so).
Please refer to the official tutorial: Authenticate with the Microsoft Graph service.
To authenticate as an application you can use the Guzzle HTTP client, which comes preinstalled with this library, for example like this:
<?php
require __DIR__ . '/vendor/autoload.php';
$guzzle = new \GuzzleHttp\Client();
$tenantId = 'your_tenanet_id, e4c9ab4e-****-****-****-230ba2a757fb';
$clientId = 'your_app_id_registered_in_portal, dc175b96-****-****-****-ea03e56da5e7';
$clientSecret = 'app_key_generated_in_portal, /pGggH************************Zr732';
$url = 'https://login.microsoftonline.com/' . $tenantId . '/oauth2/token';
$user_token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => $clientId,
'client_secret' => $clientSecret,
'resource' => 'https://graph.microsoft.com/',
'grant_type' => 'password',
'username' => 'your_user_id, jack#***.onmcirosoft.com',
'password' => 'your_password'
],
])->getBody()->getContents());
$user_accessToken = $user_token->access_token;
$graph = new Graph();
$graph->setAccessToken($user_accessToken );
$response = $graph->createRequest('Get','/me/messages')
->setReturnType(Model\Message::class)
->execute();
?>
Note that the resource should be https://graph.microsoft.com/

Categories