Cognito token being used before issued at time - php

i'm using the Cognito authorization code to get my access tokens from AWS Cognito. So this is my current workflow:
No session data, forward user to hosted UI.
On login, return to PHP Application with the authorization code.
Use authorization code to get the tokens.
Validate the tokens using the jwk tokens.
So step 4 is where i'm stuck, as when validating the tokens, JWT is starting that i'm trying to decode the access_token before it is valid:
Cannot handle token prior to 2022-05-26T13:45:17+0000
I have dumped out the code which displays this error to get the iat time and the time that it is being compared against:
dd($payload->iat, ($timestamp + static::$leeway));
This outputs:
1653576011 1653576009
So there is a 0000000002 difference...
Now I can fix this by changing leeway to 10, but this doesn't seem the correct thing to do... any ideas?
Below is the code:
private function validateTokens($accessToken): bool
{
$domain = implode('.', [ 'cognito-idp', $this->region, 'amazonaws.com' ]);
$jwks = file_get_contents("https://$domain/{$this->pool_id}/.well-known/jwks.json");
$jwks = collect(json_decode($jwks, true, 512, JSON_THROW_ON_ERROR)['keys']);
$jwks = $jwks->keyBy('kid')->map(static function ($current) {
return (new JWKConverter())->toPEM($current);
})->toArray();
$jwt = new JWT();
$jwt::$leeway = 10;
$decoded = $jwt::decode($accessToken['access_token'], $jwks, ['RS256']);
dd($decoded);

Related

Code 204 - Error while creating Webhook URL for twitter account in php

I am using twitter account activity api's to send/receive messages. In it i am facing problem in generating webhook. First time it was created successfully. In webhook file i am saving messages in database when each time message sent or received. But when I sent message nothing goes in database. Here is webhook file:
const APP_CONSUMER_SECRET = '**********';
// Example token provided by incoming GET request
if(isset($_REQUEST['crc_token'])) {
$token = $_REQUEST['crc_token'];
/**
* Creates a HMAC SHA-256 hash created from the app TOKEN and
* your app Consumer Secret.
* #param token the token provided by the incoming GET request
* #return string
*/
function get_challenge_response($token) {
$hash = hash_hmac('sha256', $token, APP_CONSUMER_SECRET, true);
$response = array(
'response_token' => 'sha256=' . base64_encode($hash)
);
return json_encode($response);
}
}else{
$feedData = file_get_contents('php://input');
$handleData = fopen('twitterDemo.txt', "w" );
fwrite($handleData,$feedData);
fclose($handleData);
$array = json_decode(file_get_contents('php://input'), true);
if (isset($array['direct_message_events'][0]['type']) && $array['direct_message_events'][0]['type'] == 'message_create' ) {
include_once ('config.php');
include_once ('database-queries.php');
$message = $array['direct_message_events'][0]['message_create']['message_data']['text'];
$sender = $array['direct_message_events'][0]['message_create']['sender_id'];
$from = $array['direct_message_events'][0]['message_create']['target']['recipient_id'];
$message_type = 'incoming';
$message_status = 'unread';
$userId = $sender;
$account_name = 'twitter';
$image_url = '';
if(isset($array['direct_message_events'][0]['message_create']['message_data']['attachment'])){
$image_url = "Not Image";
}
$data = array('to'=>$from, 'from'=>$sender, 'msg'=>$message,'image_url' =>$image_url);
insert($data, $account_name, $message_type, $message_status, $conn);
}
}
I thought there might be webhook problem so i deleted the existing app and create new one and set development environment label for it with new name. But for it when i tried to create webhook it gives me error:
[code] => 214 [message] => Webhook URL does not meet the requirements.
Invalid CRC token or json response format.
I dont know whats happening here now. I am using this api to create webhook url
$url = "https://example.com/twitter/webhook.php";
$result = $connection->post("account_activity/all/env_name/webhooks", ["url" => $url]);
Can anyone please help me this out. Any help will be appreciated.
Thanks!
I ran into the same error message.
The problem seem to be that the
if(isset($_REQUEST['crc_token']))
is not working anymore. Though the token is set. I don't know what is causing the problem but when I let the code just fetch the $_REQUEST['crc_token'] and create and print the hash its working.
But I see your code is not calling the get_challenge_response($token) function.
I think
print get_challenge_response($token)
may help? If the Webhook was deactivated by twitter you have to initiate another CRC check:
https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/subscribe-account-activity/guides/securing-webhooks
Your app can also trigger a CRC when needed by making a PUT request with your webhook id. Triggering a CRC is useful as you develop your webhook application, after deploying new code and restarting your service.

Keycloak : validating access_token using PHP

Assuming that I received token after managed to login through openid-connect
http://xxxxxx/auth/realms/demo/protocol/openid-connect/token
{
"access_token": "xxxxxx",
"expires_in": 600,
"refresh_expires_in": 1800,
"refresh_token": "xxxxxx",
"token_type": "bearer",
"not-before-policy": xxxx,
"session_state": "xxxxx",
"scope": "email profile"
}
Is there any ways on how to decode the payload of the jwt tokens just like the https://jwt.io/ did , using PHP? Thank you.
You can use this library https://github.com/firebase/php-jwt.
Why do you want to decode the access_token though? Usually it's the id_token that gets decoded so that the client can verify the end-user's identity. The process of decoding requires the JWT to have its signature verified.
You can use the library I mentioned above. The steps are easy. You need:
The JWT
Secret Key/ Public Key
Algorithm used to encode the JWT
The following snippet is used to decode + verify a JWT. It uses HS256 so a secret key must be in the possession of the client:
$decoded = JWT::decode($jwt, $key, array('HS256'));
If you want to decode a JWT without verifying its signature (unsafe), you can create a function that separates each of the JWT section: header, body, and signature, and base64url decode it. Like so:
// Pass in the JWT, and choose which section. header = 0; body = 1; signature = 2
public function decodeJWT($jwt, $section = 0) {
$parts = explode(".", $jwt);
return json_decode(base64url_decode($parts[$section]));
}
EDIT if you're decoding + verifying an id_token which uses assymetric algorithm e.g. RSA256, RSA384 etc, you need the public key. OpenID Connect defines a JWK Set endpoint (/.well-known/jwks.json), which lists the public keys in JWK format. You can hit that endpoint and save the response in an array. In order to find which public key was used, the JWK has a kid claim/ property. Which represents the key id, the identifier of the public key. You can decode your id_token and grab its header using :
$header = decodeJWT($id_token, 0);
Then you can pass the header to the function below to get the key that was used to encode the id_token. Parameter $keys holds the JWK Set response:
function getIdTokenKey($keys, $header) {
foreach ($keys as $key) {
if ($key->kty == 'RSA') {
if (!isset($header->kid) || $key->kid == $header->kid) {
return $key;
}
}
}
throw new Exception("key not found");
}
$key = getIdTokenKey($keys, $header);
Finally call the decode function, assume it's using RSA256:
$decoded = JWT::decode($id_token, $key, array('RSA256'));
Edit(2) On another note it's the same process to decode any JWT, be it an access token, id token, or arbitrary data being passed to different entities in a server environment.

Getting Invalid Signature when accessing tokens in Trello API OAuth

I am trying to implement a wrapper for trello api in PHP. I am getting "Invalid signature" in last step of OAuth process where I have to get access tokens from trello api.
Just by this message I just can't debug what am I doing wrong.
Basically what I did was...
Send a request for fetch request tokens (https://trello.com/1/OAuthGetRequestToken). This went well. I got back two parameters oauth_token and oauth_token_secret in response.
Then I opened the trello authorization page url (https://trello.com/1/OAuthAuthorizeToken) with parameters oauth_token from step 1, app name and a return url at localhost. This went well too. Trello redirected to localhost/callback with parameters oauth_token and oauth_verifier.
In callback, I finally sent request to fetch access tokens (https://trello.com/1/OAuthGetAccessToken). I added oauth_token & oauth_token_secret from step 1, oauth_verifier from step 2 and a signature using HMAC-SHA1 method. This went awry when I got a 500 internal error with message "Invalid signature"!!!
Does anybody have an idea what might be wrong?
Here is the code I used in callback.
$nonce = md5(mt_rand());
$timestamp = time();
$oauth_signature_base = 'GET&'.
rawurlencode('https://trello.com/1/OAuthGetAccessToken').'&'.
rawurlencode(implode('&', [
'oauth_consumer_key='.rawurlencode('CONSUMER_KEY_HERE'),
'oauth_nonce='.rawurlencode($nonce),
'oauth_signature_method='.rawurlencode('HMAC-SHA1'),
'oauth_timestamp='.rawurlencode($timestamp),
'oauth_token='.rawurlencode('OAUTH_TOKEN_HERE'),
'oauth_token_secret='.rawurlencode('OAUTH_TOKEN_SECRET_HERE'),
'oauth_verifier='.rawurlencode('OAUTH_VERIFIER_HERE'),
'oauth_version='.rawurlencode('1.0')
]));
$signature = base64_encode(hash_hmac('sha1', $oauth_signature_base, 'CONSUMER_SECRET_HERE&', true));
$params = [
'oauth_consumer_key='.rawurlencode('CONSUMER_KEY_HERE'),
'oauth_nonce='.rawurlencode($nonce),
'oauth_signature_method='.rawurlencode('HMAC-SHA1'),
'oauth_timestamp='.rawurlencode($timestamp),
'oauth_token='.rawurlencode('OAUTH_TOKEN_HERE'),
'oauth_token_secret='.rawurlencode('OAUTH_TOKEN_SECRET_HERE'),
'oauth_verifier='.rawurlencode('OAUTH_VERIFIER_HERE'),
'oauth_version='.rawurlencode('1.0'),
'oauth_signature='.rawurlencode($signature)
];
file_get_contents(sprintf('%s?%s', 'https://trello.com/1/OAuthGetAccessToken', implode('&', $params)));
The oauth token secret should not be included in the URL parameters, either in generating the base string or when sending the actual request. The token secret is used only as part of the hash key. See modified code below:
$nonce = md5(mt_rand());
$timestamp = time();
$oauth_signature_base = 'GET&'.
rawurlencode('https://trello.com/1/OAuthGetAccessToken').'&'.
rawurlencode(implode('&', [
'oauth_consumer_key='.rawurlencode('CONSUMER_KEY_HERE'),
'oauth_nonce='.rawurlencode($nonce),
'oauth_signature_method='.rawurlencode('HMAC-SHA1'),
'oauth_timestamp='.rawurlencode($timestamp),
'oauth_token='.rawurlencode('OAUTH_TOKEN_HERE'),
'oauth_verifier='.rawurlencode('OAUTH_VERIFIER_HERE'),
'oauth_version='.rawurlencode('1.0')
]));
//token secret should be (singly) URL encoded if not already
$signature = base64_encode(hash_hmac('sha1', $oauth_signature_base, 'CONSUMER_SECRET_HERE&TOKEN_SECRET_HERE', true));
$params = [
'oauth_consumer_key='.rawurlencode('CONSUMER_KEY_HERE'),
'oauth_nonce='.rawurlencode($nonce),
'oauth_signature_method='.rawurlencode('HMAC-SHA1'),
'oauth_timestamp='.rawurlencode($timestamp),
'oauth_token='.rawurlencode('OAUTH_TOKEN_HERE'),
'oauth_verifier='.rawurlencode('OAUTH_VERIFIER_HERE'),
'oauth_version='.rawurlencode('1.0'),
'oauth_signature='.rawurlencode($signature)
];
file_get_contents(sprintf('%s?%s', 'https://trello.com/1/OAuthGetAccessToken', implode('&', $params)));

Firebase .on not working with authentication

UPDATE
I was able to at least get the client side code to work with authentication using firebase-simple-login.js and auth.login('anonymous'). Server side (ie "write") still does not work.
ORIGINAL QUESTION
I am creating an app with Firebase integration and simply need to secure my Firebase data. Being able to delete everything if you know my Firebase URL and where to look without any authentication is obviously less than ideal.
I am not trying to log my users in (or at least not in the traditional sense), I just want to make sure that there is SOME SORT of authentication going on when I read and write from my Firebase data. I have spent hours on this and cannot seem to make it work (so much for "easy").
First, my Firebase security rules - simple
{
"rules": {
".read" : "auth != null",
".write": "auth != null"
}
}
I am pushing to Firebase from server side code and reading the results client side. This is a simple polling app - poll responses are pushed to Firebase in the following format: clients\ClientA\polls\POLLID(random)\RandomDataID(from Firebase)\Response Data. I am using firebase/php-jwt to generate my server side JWT:
<?php
class generateFirebaseToken {
public static function generate(array $data = array())
{
$key = 'FIREBASE SECRET KEY';
$token = array(
'iss' => 'https://example.com',
'iat' => time()
);
// Add additional data to token
$token = array_merge($token, $data);
$jwt = JWT::encode($token, $key);
return $jwt;
}
}
I am then pushing data to Firebase with the following code. There are several variables from the user's session included. This uses a class I wrote that prepares a CURL request for Firebase. Everything works fine if I remove the auth != null from the Firebase rules. Otherwise, nothin':
$fbData = array(
'name' => "{$this->user->first_name} {$this->user->last_name}",
'answer' => $fbAnswer,
'gravatar' => Gravatar::src($this->user->email)
);
$token = generateFirebaseToken::generate();
$fb = new fireBase("clients/{$this->client->nickname}/polls/{$poll->uniquid}.json?auth=$token", $fbData);
$fb->execute('POST');
Source of $fb->execute()
public function execute($method)
{
$data_string = json_encode($this->data);
$ch = curl_init($this->root . $this->path); // http://myapp.firebaseio.com/
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
return $result;
}
Client side also does not work. I retrieve a JWT by performing a $.getJSON request to my server side code and pass it on to Firebase. It seems to authenticate correctly but then I receive the error FIREBASE WARNING: on() or once() for /clients/exampleclient/polls/FdV4RM9LHcoB7U7W failed: Error: permission_denied: Client doesn't have permission to access the desired data.. Here is the client side code:
$.getJSON('/secure/jwt-token', function(json) {
jwtToken = json.token;
launchFirebase(jwtToken);
});
function launchFirebase(token)
{
var fb = new Firebase('https://myapp.firebaseio.com/clients/exampleclient/polls/' + pollID);
fb.auth(token, function(e) {
if(e) {
alert('Authentication error : ' + e);
} else {
alert('Authenticated'); // Alert shows, so I assume authenticated
fb.on('child_added', function(snapshot) {
// do stuff
// Error occurs here.
});
}
});
}
I am assuming I am missing something simple here, perhaps I do not understand how to use JWT.. whatever the case, any help would be appreciated. Thanks!
HOURS of wasted effort but I finally found the problem, hopefully this helps someone else new to using Firebase. Also goes to show that if you're working on the same issue for hours, take a break and the fog will begin to clear.
Okay, the problem was -- of course -- simple. I was using the firebase/php-jwt library. I had everything right EXCEPT for the fact that I did not add 'd' to the token data array - THIS IS WHERE auth COMES FROM. So, my security rules was checking for auth, missing because I did not add d to my token.
Here is the fixed code:
$key = 'YOUR-SECURITY-KEY';
$token = array(
'iss' => 'https://EXAMPLE.com',
'iat' => time(),
'd' => array(
'foo' => 'bar' // THIS GIVES YOU THE AUTH VARIABLE!!!!!
)
);
// Add additional data to token
$token = array_merge($token, $data);
$jwt = JWT::encode($token, $key);
return $jwt;
If you write code for people to plug into a service, as in this case, PLEASE provide better documentation. It seems too often that such things are just entirely omitted, perhaps because the developer should just know intrinsically? I finally discovered the answer reading the docs for making a token WITHOUT the use of a helper library (like this one).

How do I interact with Coinbase's API? Why does it always fail?

Accessing the Coinbase API used to be really simple: all you needed was an API key. Now you need something called a "nonce" and a "signature". I pass my new API "Secret", the nonce, and the Key in my request, but it returns an "invalid_key" error. What gives?
EDIT March 12th: Added a tutorial on interacting with the API via OAuth.
The fact that the API used to be so simple -- only needing a Key -- means it was pretty insecure. So they beefed up the security a week-ish ago. Here's the blog post:
http://blog.coinbase.com/post/75936737678/more-security-and-granular-control-with-the-new-api
Everyone now gets an API "Secret" in addition to an API Key. Whenever you make a request to the API, you have to include three parameters:
Your API Key.
A "nonce", which is a unique number that you use to identify something. In this case, every single request you make needs to have a new number, and each request's nonce has to be bigger than the one before it.
Your API "Signature". This is NOT your API "Secret".
The Signature is your nonce followed immediately by the full URL to which you're posting your request, parameters and all. This URL also contains the nonce, so the whole thing all together would look something like this:
12345https://coinbase.com/api/v1/buttons?nonce=12345&name=Socks&price=9.95
Then you take that whole thing and encode it as a "SHA256" hash. If you don't know what that means, don't panic -- you can do it in one line using a function PHP already has built in.
At any rate, I was having some trouble figuring all this out, so I spent a little while on it and put together this script, which makes GETing and POSTing to the API really easy. I'd love to hear people's thoughts!
<?php
function coinbaseRequest($what,$getOrPost,$parameters){
//Obviously, your API Key and Secret go here.
$apikey = "blahblahblah";
$apisecret = "blahblahblahblah";
$nonce = file_get_contents("nonce.txt") + 1;
file_put_contents("nonce.txt", $nonce, LOCK_EX);
$url = "https://coinbase.com/api/v1/" . $what . "?nonce=" . $nonce;
if($parameters != ""){
$parameters = http_build_query(json_decode($parameters), true);
}
//Here I go, hashing the Signature! Thanks, PHP, for making this easy!
$signature = hash_hmac("sha256", $nonce . $url . $parameters, $apisecret);
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => array(
"ACCESS_KEY: " . $apikey,
"ACCESS_NONCE: " . $nonce,
"ACCESS_SIGNATURE: " . $signature
)));
if($getOrPost == "post"){
curl_setopt_array($ch, array(
CURLOPT_POSTFIELDS => $parameters,
CURLOPT_POST => true,
));
}
$results = curl_exec($ch);
curl_close($ch);
echo $results;
}
//This is a POST example.
coinbaseRequest("buttons", "post", '{
"button": {
"name": "test",
"price_string": "1.23",
"price_currency_iso": "USD",
"variable_price": true
}
}');
//This is a GET example.
coinbaseRequest("account/balance", "get", false);
?>
Notes:
I tried using (microtime(true)*100) for my nonces. The problem is it makes a decimal number, and the last few digits kept getting dropped or rounded off so it didn't work. Then I thought, "Screw this", made a blank nonce.txt file, and wrote 1 in it, and to get nonces I just got the contents of that file, added 1, and replaced the file with the new number. It served a second purpose as a counter showing how many total requests I've made.
But then someone pointed out to me PHP's "uniqid" function, which generates an ID based on the current microtime. So you can also try this:
$nonce = hexdec(uniqid());
This has the advantage of not accessing an external file. I actually really like being able to see how many requests I've made, and so will probably stick with the (bad) nonce.txt method.
The coinbaseRequest() function has three parameters. The first is the directory to which you're making your request -- that is, whatever's supposed to come after "https://coinbase.com/api/v1/". The second parameter is "get" or "post", depending on whether it's a GET or a POST request. (Make sense?)
The third parameter is all the queries you're passing in your request. This should be formatted as JSON, unless it's a GET request that doesn't take any parameters (besides the Key, Nonce, and Signature which the function includes for you), in which case you should leave this as false.
EDIT, March 3rd:
I made a little function for taking whatever's returned by coinbaseRequest and turning it into a button:
function makebutt($data){
$data = json_decode($data,true);
$buttoncode = $data["button"]["code"];
return ("<a class=\"coinbase-button\" data-code=\"" . $buttoncode . "\" href=\"https://coinbase.com/checkouts/" . $buttoncode . "\">Pay With Bitcoin</a><script src=\"https://coinbase.com/assets/button.js\" type=\"text/javascript\"></script>");
}
YE GREAT OAUTH TUTORIAL
<?php
/*OAuth is great. It's also complicated. Or rather, it LOOKS complicated.
This whole script is just one big long function. It's a really, really ugly
function. I broke down everything "Barney-style" to demonstrate all the steps
in the process, and because there are some things you have to decide -- how to
record the user data, for instance.
Let's get this train wreck a rollin'.*/
function oauthRequest($apiPath,$getOrPost,$parameters){
/*You get this info from https://coinbase.com/oauth/applications*/
$clientId = "#####";
$clientSecret = "#####";
$callbackUrl = "http://www.blah.com/oauth.php";
function curling($url,$getpost,$params){
if($params != ""){
$params = http_build_query(json_decode($params), true);
}
if($getpost == "get"){
$ispost = false;
$url .= $params;
}
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
));
if($getpost == "post"){
curl_setopt_array($ch, array(
CURLOPT_POST => $ispost,
CURLOPT_POSTFIELDS => $params
));
}
$results = curl_exec($ch);
curl_close($ch);
return $results;
}
/*There are two people involved here: the Client (you), and the User (the
person accessing your app or site).
You'll need 3 pieces of data for each user before your app can access their
Coinbase account: a User Code, an Access Token, and a Refresh Token.
For the purposes of this demonstration, I'm recording all of the user data in
a .txt file on my server. THIS IS PROBABLY A BAD IDEA in real life because .txt
files aren't secure at all, and this text file will only store the data for one
user (it gets overwritten every time). This is the kind of stuff you'd put in an
SQL database if you have one, or maybe in the user's cookies.*/
if(!file_exists("userdata.txt") || file_get_contents("userdata.txt") == ""){
file_put_contents("userdata.txt",json_encode(array(
"userCode" => "",
"accessToken" => "",
"refreshToken" => ""
)), LOCK_EX);
}
$userData = json_decode(file_get_contents("userdata.txt"), true);
/*Okay. So. First thing we're going to do is see if we have a User Code for
this user. These are big alphanumeric strings that are 64 characters long. If
we have one, it'll either be in the URL of this page (the $_GET array), or
it'll be in that userdata.txt file.*/
if(array_key_exists("code",$_GET) && $_GET["code"] != ""){
$userCode = $_GET["code"];
}else if(array_key_exists("userCode",$userData) && $userData["userCode"] != ""){
$userCode = $userData["userCode"];
}else{
/*If we don't have a User Code, then this next snippet of code will fire. It'll
return the link for a special user-specific Coinbase page to which the user
will need to go to authorize your app to access their Coinbase account (by
signing into Coinbase and clicking a green "Authorize" button).
After authorizing your app, they'll be automatically taken to the Redirect URL
you specified, with their User Code added to the end of the URL. So if your
Redirect URL is www.blah.com/oauth.php, they'll go to www.blah.com/oauth.php?
code=123451234512345 .
This User Code never expires, and so theoretically the user should only need to
go to the authorization link once. However, if you don't make a way of getting
the User Code in the future (my fancy "userdata.txt" in this case) or they de-
authorized your app from within their Coinbase account, then they'll need to go
to the link again and re-authorize your app from the beginning.
I have it set up so my Redirect URL and the rest of my OAuth script are all on
the same page: www.blah.com/oauth.php . So the user will just start back at the
beginning of this script, and THIS time the script will see the User Code in
the URL (the $_GET array), and so will skip this next bit.
Whew. You with me so far?*/
return ("https:/*coinbase.com/oauth/authorize?" . http_build_query(array(
"response_type" => "code",
"client_id" => $clientId,
"redirect_uri" => $callbackUrl
)));
die;
}
/*Here I am, recording the User Code for future use in userdata.txt*/
$userData["userCode"] = $userCode;
file_put_contents("userdata.txt",json_encode($userData),LOCK_EX);
/*Alright, we've got the User Code. Now we need the Access Token -- another 64-
character string. The difference is that the Access Token expires every 2 hours
(7200 seconds). Let's see if we already have one in the userdata.txt file.*/
if(array_key_exists("accessToken",$userData) && $userData["accessToken"] != ""){
$accessToken = $userData["accessToken"];
$refreshToken = $userData["refreshToken"];
}else{
/*If we're here, it's because we don't have an Access Token for this user. We
get one by making this POST request:*/
$authorization = json_decode(curling(
"https:/*coinbase.com/oauth/token" . "?" . http_build_query(array(
"grant_type" => "authorization_code",
"code" => $userCode,
"redirect_uri" => $callbackUrl,
"client_id" => $clientId,
"client_secret" => $clientSecret
)), "post", ""), true);
if(array_key_exists("error",$authorization)){
/*If something goes wrong here, I'm going to clean out userdata.txt and ask the
user to try again.*/
file_put_contents("userdata.txt","",LOCK_EX);
die("Something went wrong. Please refresh the page and try again.");
}
$accessToken = $authorization["access_token"];
$refreshToken = $authorization["refresh_token"];
}
/*The Refresh Token is what you use to get a new Access Token once the current
Access Token has expired. The Refresh Token never expires, but can only be used
once. Anytime you get an Access Token, you'll also be given a Refresh Token.
If you don't have the Refresh Token and a working Access Token for the user,
they'll need to re-authorize your app all over again.
I'm backing up the Access Token and Refresh Token to userdata.txt*/
$userData["accessToken"] = $accessToken;
$userData["refreshToken"] = $refreshToken;
file_put_contents("userdata.txt",json_encode($userData),LOCK_EX);
/*Alright! At this point, we should have the three bits of user data we need:
the User Code, the Access Token, and the Refresh Token. So now lets try
actually making an API request.
This whole script is really just one big function called "oauthRequest". You
pass three parameters to the function: the path of the API request (everything
after https:/*coinbase.com/api/v1/), whether this API query is a GET or a POST,
and any parameters that go along with that GET or POST request. These params
first come into play here.
Let's make the API request:*/
$results = curling("https:/*coinbase.com/api/v1/" . $apiPath . "?" . http_build_query(array(
"access_token" => $accessToken
)), $getOrPost, $parameters);
/*Now we're going to make sure the request actually worked, and didn't get
rejected because the Access Token was expired. If it WAS expired, the
results should be blank. (It'll return a 401 if you want to get fancy.)*/
$resultsArray = json_decode($results);
if(count($resultsArray) < 1){
/*Looks like it did expire, so now we make a POST request using the Refresh
token, which will return a new Access Token AND a new Refresh Token.*/
$reAuthorization = json_decode(curling(
"https:/*coinbase.com/oauth/token?" . http_build_query(array(
"grant_type" => "refresh_token",
"code" => $userCode,
"refresh_token" => $refreshToken
)), "post", ""), true);
$accessToken = $reAuthorization["access_token"];
$refreshToken = $reAuthorization["refresh_token"];
/*Let's back those up to userdata.txt...*/
$userData["accessToken"] = $accessToken;
$userData["refreshToken"] = $refreshToken;
file_put_contents("userdata.txt",json_encode($userData),LOCK_EX);
/*...and try the API request all over again:*/
$results = curling("https:/*coinbase.com/api/v1/" . $apiPath . "?" . http_build_query(array(
"access_token" => $accessToken
)), $getOrPost, $parameters);
/*If it doesn't work THIS time, I'm going to clean out userdata.txt and ask
the user to try again. One of the codes probably got all mungled up.*/
$resultsArray = json_decode($results);
if(array_key_exists("error",$resultsArray)){
file_put_contents("userdata.txt","",LOCK_EX);
die("Something went wrong. Please refresh the page and try again.");
}
}
/*If, however, everything went right, then this function will return the JSON
string with the data from the API! Hooray!*/
return $results;
}
/*Here are 4 different example requests you can make.*/
/*
echo oauthRequest("account/generate_receive_address","post","");
echo oauthRequest("buttons","post",'{
"button": {
"name": "test",
"type": "buy_now",
"price_string": ".01",
"price_currency_iso": "USD"
}
}');
echo oauthRequest("prices/buy","get",'{
"qty": 1,
"currency": "USD"
}');
echo oauthRequest("account/balance","get","");
*/
?>
It's not working because Coinbase recently implemented the OAuth2 protocol. This ensures the personal information of your user is transmitted securely. I referred to this implementation several months ago when writing my own OAuth class on another project:

Categories