PHP OAuth with Reddit - php

I'm trying to enable "oauth" for Reddit and I'm following the instructions given here - https://github.com/reddit/reddit/wiki/OAuth2
It's all good until I need to make a request for the "access_token".
The documentation says I need to make a "POST" request to the following URL
https://ssl.reddit.com/api/v1/access_token
With the following as POST DATA
grant_type=authorization_code&code=CODE&redirect_uri=URI
It also says
You must supply your OAuth2 client's credentials via HTTP Basic Auth for this request. The "user" is the client_id, the "password" is the client_secret.
And I'm not sure what above means. I've tried a number of different approaches but I'm still getting 401 error. Here's my "POST" code
$data = array('client_id' => self::CLIENT_ID,
'client_secret' => self::CLIENT_SECRET,
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => $my_redirect_url);
$response = Requests::post(self::GET_OAUTH_TOKEN_URL, array(), $data);
any ideas?

You need to additionally provide an "Authorization" header in this format:
"Basic %base_64_encoded_string%"
That base 64 string starts out as "username:password" or in this case "clientid:clientsecret", and then you encode it. So your Authorization header value would end up looking something like this:
"Authorization: Basic d2hhdCB1cCBob21leSBn"

Related

Azure/Microsoft Identity Platform - Cannot Retrieve Access Token from Token Endpoint (PHP)

I'm trying to write a custom web app that utilizes the Microsoft Identity Platform to authenticate and authorize users. I'm able to successfully authenticate when calling the /authorize endpoint, and I have access to the "code" token that is returned.
I am now trying to retrieve my access token so that I can make calls to the APIs. Whenever I submit a POST request to the /token endpoint, the server returns a 400 Bad Request error. The header information that is provided contains no valuable information for troubleshooting and there is no JSON response returned so I have no idea where or what the issue is.
I'm making my call as follows:
$clientId = '00000000-0000-0000....'; // Omitted
$tenantId = '00000000-0000-0000....'; // Omitted
$grantType = 'authorization_code';
$scope = urlencode('User.Read');
$code = '......' // Obtained from authentication
$redirect_uri = 'http://localhost/smp/auth/handle';
$clientSecret = '...' // Omitted, set up in Azure App registrations under Certificates and secrets
$resource = 'api://00000000-0000-0000....'; // Omitted, set up in Azure App registrations under Overview -> App ID URI
$url = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token";
$parameters = [
'client_id' => $clientId,
'grant_type' => $grantType,
'scope' => $scope,
'code' => $code,
'redirect_uri' => $redirectUri,
'client_secret' => $clientSecret,
'resource' => $resource
];
$options = [
'http' => [
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($parameters)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if (!$result) {
exit('an error has occured');
}
PHP returns a warning that reads the following: (tenant id has been omitted):
Warning: file_get_contents(https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token): Failed to open stream: HTTP request failed! HTTP/1.1 400 Bad Request in C:\xampp\htdocs\smp\application\models\user-model.php on line 261
I've verified everything I can possibly think of.
The client and tenant ids are working (based on the successful authentication request).
The "code" is correctly retrieved from the authentication request.
Redirect Uri is the same as the one used for the authentication request.
I've tried with and without the client_secret variable. It is my understanding that this is actually required in my case.
I've tried with and without the resource variable, which is setup using the default Azure naming convention of "api://".
Please assist! I know I must be missing something but cannot figure it out. Perhaps permissions or additional setup within Azure? I'm not really sure what's wrong with my code/approach but Microsoft Identity Platform/OAuth isn't returning anything for me to work with and/or troubleshoot.
I've figured this out.
Firstly, I modified by $options variable to include the following:
$options = [
'http' => [
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query($parameters),
'ignore_errors' => true
]
];
Adding ignore_errors allowed me to see the returned JSON.
Next, it became apparent that my parameters were not being accepted because I was url-encoding all of them. My client secret, redirect uri, and client ids were all url encoded (this detail was hidden in my original post because it was just a small snippet of a much larger system). By undoing this encoding, my parameters all ended up being accepted. I suppose it makes sense because these values weren't being appended to a url but rather passed as POST values.
Lastly, the "resource" variable was not accepted (indeed, it wasn't required). Not sure if any of this is going to help anyone else but just in case, this was my fix.

Problem with Azure AD authentication - Bearer token returns only app information

I have a problem implementing Azure AD SSO for my website.
The Azure AD configuration uses a certificate instead of a secret. Everything looks to be working, but https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token returns a Bearer token which contains only app information and I'm trying to get the user information for the user who has logged in.
Can anyone provide me some information and tips how to obtain user information?
Code for getting Bearer token:
public function getAccessToken(){
$link = "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token";
$request_headers = array(
'Accept: application/x-www-form-urlencoded'
);
$post_data = array(
"client_id" => $this->clientId,
"grant_type" => "client_credentials",
"client_assertion_type" => "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion" => $this->jwToken,
"scope" => "https://graph.microsoft.com/.default",
"code" => $this->responseCode,
"redirect_uri" => $this->redirectUri,
);
$curlResponse = $this->sendCURLRequest($link, $request_headers, $post_data);
var_export($curlResponse);
}
As #juunas suggested, you should use authorization code flow rather than client credentials flow.
Now that you have got the code from Request an authorization code, next you need to Redeem a code for an access token.
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&code_verifier=ThisIsntRandomButItNeedsToBe43CharactersLong
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps. This secret needs to be URL-Encoded.
What you have used is Client credentials flow get token, in which you don't need the code.

How to exchange an Apple authorization code for an access token?

I am having a hard time figuring out how to use the sign in with Apple. The documentation is terrible and failed responses leaves us clueless. The article of Aaron Parecki (https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple) does help a little, but I seem stuck now.
At first, I generate a login URL with /auth/authorize like so:
$_SESSION['state'] = bin2hex(openssl_random_pseudo_bytes(16));
return 'https://appleid.apple.com/auth/authorize?' . http_build_query([
'response_type' => 'code',
'response_mode' => 'form_post',
'client_id' => 'com.my.service.id',
'redirect_uri' => 'https://my.app/redirect'),
'state' => $_SESSION['state'],
'scope' => 'name email',
]);
After struggling with the domain verification and return URLs, this brings me to the Apple login page and returns to my redirect_uri after succesfull login. Then, I need to authorize the token which I execute using Guzzle:
$response = (new Client)->request('POST', 'https://appleid.apple.com/auth/token', [
RequestOptions::FORM_PARAMS => [
'grant_type' => 'authorization_code',
'code' => $_POST['code'],
'redirect_uri' => 'https://my.app/redirect',
'client_id' => 'com.my.service.id',
'client_secret' => $this->getClientSecret(),
],
RequestOptions::HEADERS => [
'Accept' => 'application/json'
]
]);
return json_decode($response, true);
The client secret is generated using Firebase php-jwt (https://github.com/firebase/php-jwt) and is valid through jwt.io:
$key = openssl_pkey_get_private('file://certificate.p8');
return JWT::encode([
'iss' => 'APPLETEAMID',
'iat' => time(),
'exp' => time() + 3600,
'aud' => 'https://appleid.apple.com',
'sub' => 'com.my.service.id',
], $key, 'ES256', 'certificate-id');
However, executing the token request to Apple returns a 400 error with the message 'invalid_client'. I cannot figure out if my client-id/secret is wrong or the code from the redirect is invalid. Can someone point me in the right direction?
EDIT:
Please note that I override the JWT class, allowing ES256 to be used. For more information, check this open pull request.
Based on what you said, and what Apple said in its documentation, I think you mistook Apple's Identity token with Client Secret.
Apple's Identity Token is a JWT token that uniquely identifies a user and is a layer of security "above" the existing OAuth 2.0 framework.
When you register/verify your client within an authorization server, based on the RFC 6749 - The OAuth 2.0 Authorization Framework, you most probably receive a secret from the authorisation server which is your client's secret (namely, a password for your server at com.my.service.id) to be used in EVERY request from obtaining tokens to using identities to retrieve resources. The suggested method to use this client_id/client_secret pair is HTTP Basic Authentication method, and using a POST parameter is discouraged.
The authorization server MUST support the HTTP Basic
authentication scheme for authenticating clients that were issued a
client password.
...
Including the client credentials in the request-body using the two
parameters is NOT RECOMMENDED and SHOULD be limited to clients unable
to directly utilize the HTTP Basic authentication scheme (or other
password-based HTTP authentication schemes). The parameters can only
be transmitted in the request-body and MUST NOT be included in the
request URI.
You will receive the user's Identity Token in a form of JWT token and you should provide THAT exactly and not tampered with, for this token is issued by Apple itself. You may, however, inspect the payload (see JWT Introduction at jwt.io) to know about the validity (iat + exp) of Identity Token. You should not generate an Identity Token unless you are coding for Apple's Authorization Team.
In the end, I cannot stress enough to read the OAuth spec thoroughly and remember that Identity Token is Apple's way of sending you a user's ID, but client_id and client_secret are something defined in OAuth spec.

OneDrive file access with php using microsoft graph

I tried using the library from krizalys for an implementation to read and write files from OneDrive. It should work for business accounts but would be nice if it could also work for personal accounts.
Since I read that the Live SDK used in krizalys example will be offline soon (as mentioned here), I tried implementing Microsoft Graph instead.
I implemented two ways to get an access token at the moment, one with grant type password (getAccessToken Method from this sample used) and one with client_credentials (Like in the krizalys lib). Both seem to work and return an access_token and refresh_token, but when I try to make a request I get the message:
"InvalidAuthenticationToken [message] => Access token is empty"
The code for my request:
$data = array("name" => "test");
$url = "https://graph.microsoft.com/v1.0/me/drive/root";
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', $url, [
'headers' => [
'Authorization: Bearer ' . $this->_state->token->data->access_token,
'Content-Type: application/json',
'Content-Length: ' .strlen(json_encode($data))
],
'body' => json_encode($data),
]);
I also tried it with the GET method and added the Host: graph.microsoft.com to ensure that this is not the problem:
$url = "https://graph.microsoft.com/v1.0/me";
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', $url, [
'headers' => [
'Authorization: Bearer ' . $this->_state->token->data->access_token,
'Host: graph.microsoft.com'
],
]);
The token response payload looks like this:
The application is configured at https://apps.dev.microsoft.com and the permissions are set. Is there anything wrong with my request? I have no idea why I always get the InvalidAuthenticationToken message. Thanks.
You've registered your application in the v2 Endpoint (apps.dev.microsoft.com) but the sample code you're using is for the v1 Endpoint. These are not interchangeable. Also, password isn't a valid OAuth Grant for the v2 Endpoint (v2 supports authorization_code, implicit, and client_credentials)
You need to obtain your token from the v2 Endpoint. You might find these articles helpful:
Microsoft v2 Endpoint Primer.
Azure AD v2.0 Overview

Where is my Google+ oauth2 state parameter in the redirect uri?

I'm making an OpenID / oAuth2 login process for a web app I'm building.
I am able to make an authorization request to Google+. There are several parameters returned in my redirect_uri:
access_token = averylongstring
token_type = Bearer
expires_in = 3600
id_token = anextremelylongstring
refresh_token = 1/Jo_tXhJtL3sQTBZURWyKWwebQSjxY1Rb-7sflDC74Pw
created = 1370140758
Then I perform a cURL GET that looks something like:
$url = 'https://www.googleapis.com/oauth2/v1/userinfo';
$curl_data = "?access_token=".$tokens['access_token']."&code=".urlencode('xxxxxxxxx.apps.googleusercontent.com')."&client_id=".urlencode('xxxxxxxxx.apps.googleusercontent.com')."&client_secret=".urlencode('xxxxxxxxx')."&redirect_uri=".urlencode('http://www.mydomain.com/oauth2callback.php')."&grant_type=authorization_code";
This gives me an id, which I presume is the successful conclusion to my authentication flow.
So, where did I lose my state variable between the
https://accounts.google.com/o/oauth2/auth?response_type=code&redirect_uri=http%3A%2F%2Fwww.mydomain.com%2Foauth2callback.php&client_id=xxxxxxxxxxxxxx.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.login&access_type=offline&approval_prompt=force&state={%22cid%22%3A%22%22%2C%22tid%22%3A%221370142477%22%2C%22app%22%3A%22anappname%22%2C%22Provider%22%3A%22%22}
...and my redirect_uri?
Looks like there are two redirects going to my callback url. In the first redirect, immediately following when the user allows access to their Google information, the url variables "state" and "code" are sent to my callback page. But then another call, the final authentication request, is made from my callback page, and the result of that is just a clean url without a query string.
So, during the first redirect to my callback page, I read the GET variables, and turn them into SESSION variables. Next, a cURL is performed (and I still don't have this part right, as I am making a cURL post to:
https://accounts.google.com/o/oauth2/token
...and my cURL code looks like:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"$url");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array('access_token' => $access_token,
'code' => $_REQUEST['code'],
'client_id' => 'myclientid.apps.googleusercontent.com',
'client_secret' => 'mylittlesecret',
'redirect_uri' => 'http://www.mydomain.com/oauth2callback.php',
'grant_type' => 'authorization_code',
'approval_prompt' => 'force')));
getting a:
{ "error" : "invalid_request" }
I really derive no joy in this part of my coding project.

Categories