I am trying to authenticate a user using the php-github-api library. So far I have sent the user to Github to allow my application access and I successfully get a token back. I'm not sure what to do now. Here is my code.
The URL I send the user to Github with.
https://github.com/login/oauth/authorize?scope=repo,user&client_id=<client_id>
Then with the php-github-api I am doing this. The $token variable is the code that is sent in the $_GET array when the user is redirected to the callback.
$client = new \Github\Client();
try {
$auth = $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN);
} catch (Exception $e) {
dp($e);
}
Does anyone know if this is the correct method to authenticate a user? When I try and call a method the requires an authenicated user I get a 401 status code and an error in return.
Thanks in advance!
Thanks everyone for their suggestions. Seems like you have to feed the access_token into the authenticate method so an easy fix I implemented was a CURL request to grab the access_token then adding it to the authenticate method in the callback.
$token = $_POST['token'];
$params = [
'client_id' => self::$_clientID,
'client_secret' => self::$_clientSecret,
'redirect_uri' => 'url goes here',
'code' => $token,
];
try {
$ch = curl_init('https://github.com/login/oauth/access_token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
$headers[] = 'Accept: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
} catch (\Exception $e) {
dp($e->getMessage());
}
Then in the call back we can call the authenticate method to and cache it somewhere, currently I am doing this in the session.
$client = self::getClient();
$_SESSION['access_token'] = $response->access_token;
try {
$client->authenticate($response->access_token, Github\Client::AUTH_HTTP_TOKEN);
} catch (\Exception $e) {
dp($e->getMessage());
}
So there we have it.
I did try using the HttpClient of the php github api library but I was having some issues so chose a more simple solution.
The problem is that you're using the code you receive after the user authenticates as a $token when you're supposed to use it to get an actual token. Make a post request to https://github.com/login/oauth/access_token with the client_id, client_secret, code (what you were using as the token), state, and redirect_uri.
You'll get back a response in this format access_token=e72e16c7e42f292c6912e7710c838347ae178b4a&scope=user%2Cgist&token_type=bearer
There is this code in the HttpClient.php file that would make getting the token easier than cURLing
public function post($path, $body = null, array $headers = array())
{
return $this->request($path, $body, 'POST', $headers);
}
https://developer.github.com/v3/oauth/#github-redirects-back-to-your-site
Related
I try to set my invision board api with OOAuth but I have always this message { "errorCode": "3S290\/B", "errorMessage": "NO_SCOPES" }"
What I insert inside the application
Client type :
Custom Confidential OAuth Client
A server-side app such as a website where the code will be written in a server-side language and stored on a server that no end-user has access to. A client secret will be issued.
Available Grant Types Required (check all the boxes for my test)
Authorization Code
The end-user will be shown a login screen and redirected back to a specified Redirection URI with an Authorization Code in the query string which you will then exchange for an Access Token.
Implicit
The end-user will be shown a login screen and redirected back to a specified Redirection URI with an Access Token in the fragment.
Resource Owner Password Credentials
The end-user will enter their username or email address and password which you will exchange for an Access Token.
Client Credentials
You will make API calls directly with the Client Identifier and Client Secret without any end-user logging in.
Require PKCE for Authorization Code grant?
Not required
Redirection URIs
https://www.example.com/oauth/callback/
*authorization Prompt
If the user has previously authorized, they will be redirected back immediately, without seeing an authorization screen.
Allow users to choose scopes? and Show in Account Settings? ==> no activated
About the scope :
key : profile
Authorized User : access GET selected
/downloads/categories : acces GET selected
Now about my script
/**
* #return mixed
*/
public function getToken()
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->communityUrl . 'oauth/token/');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'grant_type' => 'client_credentials',
'client_id' => 'xxxxxxxxxxxxx',
'client_secret' => 'xxxxxxxxxxxxx',
]));
// execute cURL
$result = curl_exec($ch);
curl_close($ch);
// decode JSON response
$response = json_decode($result);
return $response->access_token;
}
}
public function getAllCategories()
{
$token = $this->getToken();
if ($token !== null) {
$curl = curl_init($this->communityUrl . 'api' . $this->endpointCategories);
$array = [
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_USERAGENT => "MyUserAgent/1.0",
CURLOPT_HTTPHEADER => array( "Authorization: Bearer {$token}" ),
];
curl_setopt_array($curl, $array);
$response = curl_exec($curl);
$result = json_decode($response, true);
var_dump($response); // the response about the api to display the categories
exit;
return $result;
}
}
I have no idea why I have this message, I suppose there is something happen somewhere
Little help will be welcome
Than you
I am trying to resolve an azure marketplace subscription using the azure fulfilment api. I have followed the instructions on the Microsoft's SaaS fulfilment api's docs but I am not able to resolve the subscription using PHP.
I am able to get access_token and incidentally I am able to use the token and and use this together with the purchase identification token i get from azure portal when the subscriber is re-directed to the SaaS landing page, to get a successful json response when using postman.
I cannot achieve the same success when using PHP. I get a 403 error - Authorization is missing, incorrect or invalid. I am thinking that the query string bit of authorization parameter is malformed. This has nothing to do with privileges or permissions as I am able to get a successful output on Postman. Here is the code
<?php
use Microsoft\Graph\Graph;
use Microsoft\Graph\Http;
use Microsoft\Graph\Model;
use GuzzleHttp\Client;
class GraphHelper {
private static Client $tokenClient;
private static Client $tokenWebClient;
private static string $clientId = '';
private static string $tenantId = '';
private static string $clientSec = '';
private static string $graphUserScopes = '';
private static Graph $userClient;
private static string $userToken;
private static string $resolveToken;
private static string $subToken= '';
public static function initializeGraphForUserAuth(): void {
GraphHelper::$tokenClient = new Client();
GraphHelper::$clientId = $_ENV['CLIENT_ID'];
GraphHelper::$clientSec = $_ENV['CLIENT_SECRET'];
GraphHelper::$tenantId = $_ENV['TENANT_ID'];
GraphHelper::$graphUserScopes = $_ENV['GRAPH_USER_SCOPES'];
GraphHelper::$userClient = new Graph();
}
public static function getUserToken(): void {
//getting the access token
$accessCodeRequestUrl = 'https://login.microsoftonline.com/'.GraphHelper::$tenantId.'/oauth2/token';
$tokenRequestUrl = 'https://marketplaceapi.microsoft.com/api/saas/subscriptions/resolve?api-version=2018-08-31';
$subToken = $_SESSION['subToken'];
$tokenResponse = GraphHelper::$tokenClient->post($accessCodeRequestUrl, [
'form_params' => [
'client_id' => GraphHelper::$clientId,
'grant_type' => 'client_credentials',
'client_secret' => GraphHelper::$clientSec,
'resource' => '20e940b3-4c77-4b0b-9a53-9e16a1b010a7'
],
// These options are needed to enable getting
// the response body from a 4xx response
'http_errors' => false,
'curl' => [
CURLOPT_FAILONERROR => false
]
]);
if ($tokenResponse->getStatusCode() == 200) {
// Return the access_token
$responseBody = json_decode($tokenResponse->getBody()->getContents());
GraphHelper::$resolveToken = $responseBody->access_token;
$resolveAccessToken= $responseBody->access_token;
} else if ($tokenResponse->getStatusCode() == 400) {
// Check the error in the response body
$responseBody = json_decode($tokenResponse->getBody()->getContents());
if (isset($responseBody->error)) {
$error = $responseBody->error;
// authorization_pending means we should keep polling
if (strcmp($error, 'authorization_pending') != 0) {
throw new Exception('Token endpoint returned '.$error, 100);
}
}
}
//resolving the subscription
$resolveResponse = GraphHelper::$tokenClient->post($tokenRequestUrl, [
'form_params' => [
'content-type' => 'application/json',
'authorization' => 'Bearer '.$resolveAccessToken,
'x-ms-marketplace-token'=> $subToken
],
// These options are needed to enable getting
// the response body from a 4xx response
'http_errors' => false,
'curl' => [
CURLOPT_FAILONERROR => false
]
]);
//test whether there is a reponse
return $resolveResponse->getStatusCode(); // this returns a 403 - Authorization is missing, incorrect or invalid.
}
}
?>
I figured out that I needed to use cUrl for option to include authorization and other headers. Postman did this automatically hence the reason I was able to get results with postman and not with PHP. Eventually this code did it for me.
$subToken = rawurldecode($_SESSION['subToken']);
$ch = curl_init ();
curl_setopt ($ch, CURLOPT_URL, $tokenRequestUrl);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_HTTPHEADER, array ('Authorization: Bearer '.GraphHelper::$resolveToken,
'Content-type: application/json',
'X-ms-marketplace-token:'. $subToken));
curl_setopt($ch, CURLOPT_POST, true);
$results = json_decode (curl_exec ($ch), 1);
if (array_key_exists ('error', $results)){
echo ($results['error']);
die();
}
curl_close($ch);
return $results['subscriptionName'] ;
I found out that the endpoint was not accepting a get request. It threw an error of Subscription resolve not found","target":"subscriptionId","code":"EntityNotFound . However including a post option corrected that and no error was thrown after this. Again when decoding the token from the url - don't use urldecode() use rawurldecode () instead.
vv Post Request vv
$code = $_GET['code'];
$headers = [
"Content-Type: application/x-www-form-urlencoded",
"Accept: application/json",
];
$data = <<<DATA
{
"grant_type": "authorization_code",
"code": $code,
"client_secret": $clientsecret,
"client_id": $clientid
}
DATA;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,'https://discord.com/api/oauth2/token');
curl_setopt($ch,CURLOPT_POST,true);
curl_setopt($ch,CURLOPT_RETURNHEADERS,true);
curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
echo curl_exec($ch);
This code is supposed to return the auth code of a oauth2 auth;
{ "access_token": "6qrZcUqja7812RVdnEKjpzOL4CvHBFG", }
It's supposed to return that. Nothing is being returned on my post request.
Currently, I am trying to oauth2 authorize users in my discord server.
This isn't working, when I send the request and try to print it.
Nothing is returned. My secret client code and client ids are correct.
I don't know what the issue is. Can someone please help me with my issue?
I have tried different ways of sending the request
Needless to say, it still doesn't work.
Please someone help me. I am new with PHP and I don't fully understand all of the issues.
JSON data is not supported on this endpoint, try "Content-Type: application/x-www-form-urlencoded"... Also you're missing the redirect URL
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).
The manual is unclear as to how to implement this (it assumes you already know what exactly you're doing and in some cases feels like an afterthought), and I've been scratching my head for a fair while trying to figure it out.
The problem: authentication via HTTP auth headers for all API requests
As far as I've been able to test, I can use Basic auth and the normal form based login in CakePHP, but only by first hitting up the login action I define in the Auth component. This is fine when I'm accessing the site directly, and works as expected (with the exception of Digest, which appears to be utterly buggered). Via cURL, though, I've had no luck unless I'm already logged in.
Obviously, for an API, this is far from ideal. I don't want to post a request to /login before doing what I want to do, and I can't expect a user to log in manually so Cake has a cookie to read. It needs to be stateless.
Any attempt to supply authentication credentials along with each request I make (via cURL) is ignored and I get a 403 error in return. Neither the login method or any of the Auth classes are touched.
What do I need to do to make Cake behave like an actual API and allow me to authorise statelessly on a per request basis? Am I going to have to roll my own solution?
I have a centralized API that allows for user authentication via HTTP Digest and requires users to login for many user related functions. The way CakePHP forces a login is by checking if the action requires login, redirecting to your login action (defaults to /users/login), then you can redirect back.
I created my API by doing the following:
//Config/routes.php
///////////////////////////
/**
* Users Controller routes for REST API
*/
Router::mapResources('users');
/**
* Parses extensions for data serialization
*/
Router::parseExtensions();
//Controller/UserController.php
////////////////////////////////
<?php
App::uses('DigestAuthenticate', 'Controller/Component/Auth/');
class UsersController extends AppController {
var $name = 'Users';
//Login callback
function login() {
//dont render for login, just a call back for auth
$this->autoRender = false;
if ($this->Auth->login()) {
$this->redirect($this->Auth->redirect());
}
}
//GET /users.[xml|json]
//this is the basic call that tests user authentication
//basically a login then echo
function index() {
if ($this->Auth->login()) {
$user = $this->Auth->user();
$this->User->id = $user['id'];
$this->User->saveField('last_login', date('Y-m-d H:i:s'));
$this->set('response', array(
'response' => array(
'code' => 'users_auth_success',
'message' => 'User has passed authentication',
'data' => $user
)
));
//will serialize to xml or json based on extension
$this->set('_serialize', 'response');
}
}
}
?>
You can then use this API in something like:
$c = curl_init($uri . '.' . $this->_format);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_USERPWD, $login['user'] . ':' . $login['pass']);
curl_setopt($c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($c);
$info = curl_getinfo($c);
curl_close($c);
if($info['http_code'] == $this->_http_codes['OK']) {
//success
if($this->_format == 'xml')
$response = Xml::toArray(Xml::build($response));
else//JSON
$response = json_decode($response);
return $response['response']['data'];
} else if($info['http_code'] == $this->_http_codes['Unauthorized']) {
return false;
} else {
return null;
}