I built and maintain a PHP web application with an existing set of users. Authentication is username password, within the application.
There is now a requirement to provide access to a large new set of users, with existing Azure AD accounts. The client wants these users to be able to login using their Azure identities. The existing users would continue to authenticate the way they currently do.
I assumed this would be similar to Facebook/Google etc. SSO , but I'm struggling to find any examples of this in the Microsoft resources, or any libraries out there that will enable this. Is what I describe a valid use case, and achievable with Azuer AD Authentication?
Approach 1: Basically, to access the resources via Azure AD from PHP web application, you can refer to Web Application to Web API
To integrate Azure AD in PHP web applications, we need to follow authorization code grant flow steps to build several custom HTTP requests. E.G. To get access token via OAuth 2.0 protocol, we should refer to the steps on Authorization Code Grant Flow. generally, we will build 2 HTTP requests to get access token:
Request an authorization code.
Use the Authorization Code to Request an Access Token:
Please check this PHP test project for your reference
Approach 2 :
Please refer this github code:https://github.com/CoasterKaty/PHPAzureADoAuth
Try with these steps
Create app registration Azure AD > App registrations and click New registration.
2)After creating app registration Copy the client ID and tenant ID, pasting them into _OAUTH_SERVER and _OAUTH_CLIENTID in config.inc. The _OAUTH_SERVER entry should be the login.microsoftonline.com URL but with TENANT_ID replaced with your directory (tenant) ID
3)add a new secret and select the appropriate time. Don’t forget you will need to update this before it expires, so make a note in your calendar. Once done, copy the secret value and paste this into _OAUTH_SECRET within config.inc
4)After that able to browse to your application and be prompted to log in.. On your first go, you’ll be asked to allow permissions for everyone on your tenant (assuming you have the appropriate admin rights).
After registering the azure ,You can refer this code for a post request
eg:
<?php
$appid = "xxx";
$tennantid = "xxx";
$secret = "xxx";
$login_url ="https://login.microsoftonline.com/".$tennantid."/oauth2/v2.0/authorize";
session_start ();
$_SESSION['state']=session_id();
echo '<h2><p>You can Log In with Microsoft</p></h2>';
if ($_GET['action'] == 'login'){
$params = array (
'client_id' =>$appid,
'redirect_uri' =>'https://example/',
'response_type' =>'token',
'response_mode' =>'form_post',
'scope' =>'https://graph.microsoft.com/User.Read',
'state' =>$_SESSION['state']);
header ('Location: '.$login_url.'?'.http_build_query ($params));
}
if (array_key_exists ('access_token', $_POST)){
$_SESSION['t'] = $_POST['access_token'];
$t = $_SESSION['t'];
$ch = curl_init ();
curl_setopt ($ch, CURLOPT_HTTPHEADER, array ('Authorization: Bearer '.$t, 'Conent-type: application/json'));
curl_setopt ($ch, CURLOPT_URL, "https://graph.microsoft.com/v1.0/me/");
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
$rez = json_decode (curl_exec ($ch), 1);
if (array_key_exists ('error', $rez)){
var_dump ($rez['error']);
die();
}
}
Related
I have propblem with Onboard locations with Business Profiles.
I have working My Business App where i can make actions on Locations, reviews etc.
Also I added in Business Communications Brand, Agent and Location with id from GMB.
Now I have problem with 'request agent verification':
$dir = "service_account_from_business_communications.json";
$accessToken = exec($dir . '/oauth2l fetch --json ' . $dir . '/config/'.$file.' --scope business.manage,businesscommunications,businessmessages');
$data = [...];
$brandId = "...";
$agentId = "...";
$curl = curl_init("https://businesscommunications.googleapis.com/v1/brands/$brandId/agents/$agentId:requestVerification");
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'User-Agent: curl/business-messages',
'Authorization: Bearer ' . $accessToken,
));
Varification is added but mail is sent, and it shouldn't:
the verification process won't send an email to the brand if the agent name matches the Business Profile name and if you obtained the OAuth token with the correct scopes.
So OAuth Token isn't correct, so now im trying wit my GMB application, by adding new businesscommunications scope.
So according to docs:
In your existing OAuth consent flow where a Business Profile account owner grants you access to call the Google My Business APIs on their behalf, update the API scopes to include:
https://www.googleapis.com/auth/business.manage
https://www.googleapis.com/auth/businesscommunications
When Im adding new scope businesscommunications to my app (all working with GMB stuff) I have error on OAuth Login saying i have incorrect scope and it is not enabled in project:
Business Communications API has not been used in project XXX before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/businesscommunications.googleapis.com/overview?project=XXX then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
But that scope not exists in API Library.
I need to add and the end that when I set agent and location in Business Communications in panel i can send replies on messages by API but not that from My Business Locations.
How can I combine these two working separate scopes and the API?
"Business Communications" is an umbrella term, not an individual API ...
A search query turns up a few "My Business" API, but no "Business Communications" API:
https://console.cloud.google.com/apis/library/browse?project=_&q=My%20Business
But when browsing the documentation, there's a link to the relevant console page:
https://business-communications.cloud.google.com/console/
One can only request individual scopes, as there is no such scope.
From a PHP perspective, I'd suggest to also obtain the access token with cURL.
I'm using Firebase to do a small project and while testing things I discovered I can do cURL requests from any server to my Firebase Database (tested on an online php tester), so I'm considering this is a security flaw for my project and I have been looking for a method to add some kind of password for cURL requests, but I found nothing, at least nothing I could understand. I know firebase have rules to manage who can read or write on my database, but I didnt find something that could filter requests by server or only allow requests that have an special password sent as parameter.
So my question is if there is a way to do something like that I could use on my project so only cURL requests made for me would work.
Here it is one of my cURL requests, in case it helps for resolving my problem.
$url = "https://mydatabase.firebaseio.com/profile/messages/".$_COOKIE['cookiename'].".json";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
Thanks in advance for helping me out.
UPDATE: I found this, I think it could be the thing I need, but Im missing the part where I tell the database to ask for the access token. https://firebase.google.com/docs/database/rest/auth
One solution is to use the Firebase Auth REST API.
In particular, "you can sign in a user with an email and password by issuing an HTTP POST request to the Auth verifyPassword endpoint", see here.
Then you can use the user's uid in your Firebase security rules, in order to protect your database.
You should read and understand the documentation for the REST API. If you want to bypass security rules that would normally apply to web and mobile users, you will need to generate an OAuth token for a service account that has permissions to access your database, and use that in your requests.
If you don't want public access to your database, you will have to set up security rules to limit that. To stop all public access, your rules should be:
{
"rules": {
".read": false,
".write": false
}
}
I want to find the list of friends of Google+ user who have authorized the app without asking for authorization again.
For example: Once the user login using the Google+ to my website, I store the auth token, code, Google+ user id in the db.
So once that is done I want to find the list of users friends in his circle.
I managed to get the first part working and it saves the auth token, code, Google+ user id in the db.
But in the second case i.e. finding the list of users friends, I again get a the screen where user have to authorize the app.
Can someone please help me in this?
Also is it possible to get all the user details in offline mode like using the auth token, code, Google+ user id which is in the db?
Initial code(login):
require_once __DIR__.'/social-api/google/Google_Client.php';
require_once __DIR__.'/social-api/google/contrib/Google_PlusService.php';
$this->sn_obj = new Google_Client($google_app_id, $google_secret_key, $call_back_url);
$this->plus_obj = new Google_PlusService($this->sn_obj);
$this->sn_obj->setRedirectUri($call_back_url);
$this->sn_obj->setState($redirect_url);
$requestVisibleActions = array('http://schemas.google.com/AddActivity','http://schemas.google.com/ReviewActivity');
$this->sn_obj->setRequestVisibleActions($requestVisibleActions);
$this->sn_obj->setAccessType('offline');
$this->sn_obj->setScopes(array('https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email','https://www.googleapis.com/auth/plus.login'));
$this->sn_obj->createAuthUrl();
In callback page:
require_once __DIR__.'/social-api/google/Google_Client.php';
require_once __DIR__.'/social-api/google/contrib/Google_PlusService.php';
$this->sn_obj = new Google_Client($google_app_id, $google_secret_key, $call_back_url);
$this->plus_obj = new Google_PlusService($this->sn_obj);
$this->sn_obj->setRedirectUri($call_back_url);
$this->sn_obj->setState($redirect_url);
$requestVisibleActions = array('http://schemas.google.com/AddActivity','http://schemas.google.com/ReviewActivity');
$this->sn_obj->setRequestVisibleActions($requestVisibleActions);
$this->sn_obj->setAccessType('offline');
$this->sn_obj->setScopes(array('https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email','https://www.googleapis.com/auth/plus.login'));
$this->sn_obj->authenticate();
$google_auth_token = $this->sn_obj->getAccessToken();
$google_user_info = $this->plus_obj->people->get('me');
Friends listing page:
require_once __DIR__.'/social-api/google/Google_Client.php';
require_once __DIR__.'/social-api/google/contrib/Google_PlusService.php';
$this->sn_obj = new Google_Client($google_app_id, $google_secret_key, $call_back_url);
$this->plus_obj = new Google_PlusService($this->sn_obj);
$this->sn_obj->setRedirectUri($call_back_uri);
$this->sn_obj->authenticate();
$user_info = $this->plus_obj->people->listPeople($token['oauth_uid'],'visible');
In general, when working in offline mode, you need to store the entire object that comes back from the server - not just the auth token. This object includes the long-lived refresh token as well as information about when it needs to do a refresh. Passing this entire token to the service allows it to refresh the token for you.
The answer you came up with looks like you do the refresh yourself manually - which is fine, but shouldn't be necessary in general.
In some cases, if a user authorizes your app before you have requested offline mode, and you later request offline mode, you still won't get the refresh token. Google will only give a refresh token when asked if there is no auth token, current or expired, that was issued. There are two ways around this:
When you switch to using offline mode, invalidate your old client id / secret and start using a new one. or
Have the user de-authorize your app via https://accounts.google.com/b/0/IssuedAuthSubTokens or (if you're using Google+ Sign-In) via https://plus.google.com/apps
Finally managed to get get it done
On Friends listing page:
require_once __DIR__.'/social-api/google/Google_Client.php';
require_once __DIR__.'/social-api/google/contrib/Google_PlusService.php';
$this->sn_obj = new Google_Client($google_app_id, $google_secret_key, $call_back_url);
$this->plus_obj = new Google_PlusService($this->sn_obj);
if(!$this->sn_obj->getAccessToken()){
$this->sn_obj->refreshToken($refresh_token_from_db);
}
$google_auth_token = $this->sn_obj->getAccessToken();
$google_auth_token_arr = json_decode($google_auth_token,true);
$url = 'https://www.googleapis.com/plus/v1/people/'.$uid_from_db.'/people/visible?access_token='.$google_auth_token_arr['access_token'].'&format=json';
$c = curl_init($url);
curl_setopt($c, CURLINFO_HEADER_OUT, 1);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 0);
$contents = curl_exec($c);
curl_close($c);
$user_info = json_decode($contents, true);
I have spent quite some time now trying to establish how, and then the best practise to push some data from my web server to the facebook page created for this purpose.
I have read and understand the process of using access tokens. I have generated an access token for myself, which can be used to post to the page as me ok. I understand this should be used to generate the access token for the page to post as the page which is a ittle more tricky. However, this process involves me logging in and generating an access token which seem inherently bad / inconvenient for an automated process.
For this reason i followed the guides to create an app. I (think I have) linked the app to the page, and thus attempt to push data via the appid and secret from my php code to the page.
When doing this I am presented with this error
{"error":{"message":"(#210) Subject must be a page.","type":"OAuthException","code":210}}
my testing code is this:
$data['picture'] = "http://www.example.com/image.jpg";
$data['link'] = "http://www.example.com/";
$data['message'] = "Your message";
$data['caption'] = "Caption";
$data['description'] = "Description";
$data['access_token'] = $app_token2;
$post_url = 'https://graph.facebook.com/'.$app_id.'/feed';
$url1 = "https://graph.facebook.com/endpoint?key=value&access_token=".$app_id."|". $app_secret ;
echo "<br>$post_url";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $post_url);
//curl_setopt($ch, CURLOPT_URL, $url1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$return = curl_exec($ch);
curl_close($ch);
echo "$return";
I appreciate using the app for server initiated posting using a permanent (ish) secret is the correct way, but I have found very little documentation on how this process is achieved, hence this question.
Your $post_url is pointing to your $app_id variable, the message says it should point to a page, try getting the id of your page from the /me/accounts endpoint of the graph and putting that in there instead. Though I suspect you will need to use a page access_token (also from the /me/accounts endpoint ) to post to your page
Right, I have worked on this for quite some time and found several errors in my code, but have not answered the question fully.
For starters, you do not post to the appid as mentioned above - its just wrong. The code for posting to userid/feed works when using an access token generated from appid + secret using
$url2 = "https://graph.facebook.com/oauth/access_token?client_id=".$app_id."&client_secret=". $app_secret."&grant_type=client_credentials";
this app access token is valid for as long as your app secret is. Also, if I generate a temporary access code for ME via the graph explorer, parse me/accounts manually and use the page token in
$post_url = 'https://graph.facebook.com/'.$page_id.'/feed';
It posts correctlly as the page.
Secondly, all server side call are required to have appsecret_spoof in them which is generated from
$appsecret_proof= hash_hmac('sha256', $app_token2, $app_secret);
now, according to the facebook docs, a http get call to my userid/accounts should yield page access tokens for all pages the user administers (and also presumably the app).
This can also be called directly by using
$url3 = "https://graph.facebook.com/".$page_id."?fields=access_token";
so when a get is made to this address (including app access token & appsecret_spoof) all i get is 'True' ??
Likewise, when the correct call to
$rob1 = "https://graph.facebook.com/".$user_id."/accounts";
I receive an error
{"error":{"message":"(#10) Application does not have permission for this action","type":"OAuthException","code":10}}
OK permission issue then ! Well the documentation says that only manage_pages is required to retrieve the page token from page_id/accounts, so I trawl through lots of pages and find you can do this by calling this url
https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=https://www.facebook.com/connect/login_success.html&response_type=token&scope=read_stream
This throws up via facebook gui an accept / deny page for each scope (i ended up adding manage_pages, publish_stream & read_stream to my app) none of which seem to solve the problem !
Right sorted !! FWIW the code above functions correctly, however the way it is setup on facebook leaves a lot to be desired !!!
I began messing with my app and changed its name, type (was an app linked to the page - now isnt) removed perms and also changed the domain name (removed) and all site url details (also removed). this prompted a different error msg which stated the domains did not match. So, I readded just the app domain & site url, saved and all of a sudden my failed code started working !
Having tidied my code up a little I can now see the page access token just fine as I expected. I just wish the facebook guides would cross reference this setup as it is not at all obvious !!!
my working code ended up as thus ($perm_url is used as one time link to allow perms via gui)
$perm_url = "https://www.facebook.com/dialog/oauth?client_id=".$appid."&redirect_uri=https://www.facebook.com/connect/login_success.html&response_type=token&scope=publish_stream,manage_pages,read_stream,status_update";
echo "<br>Test";
$facebook = new Facebook(array('appId' => $appid , 'secret' => $appsecret ,));
$access_token = $facebook->getAccessToken();
$pageinfo = $facebook->api("/".$page_id."?fields=access_token");
$page_access_token = $pageinfo['access_token'];
echo "<br>Holy Grail = $page_access_token ";
I am writing a PHP application that's supposed to allow users to add certain events to a private Google Calendar. The calendar is owned by me, and I need a way for PHP to communicate with the calendar API using fixed credentials (everyone can add events using a form on the website, but the calendar itself is not publicly visible).
From what I have read, this is possible using ClientLogin in the v1 API. In the v3 API, however, the available options are OAuth2.0 or the API key. Using the API key doesn't seem to work, since it can only be used for requests that don't require authorization, and OAuth doesn't seem right either, because users are not supposed to access their own calendars, but the one my application uses.
I thought about getting the OAuth token programatically, but that's bound to break sooner or later, since the OAuth dialog can use captchas.
This seems to be such a standard use case — a web application that lets users interact with a single calendar in some predefined ways — yet I can't find any documentation on how to make it happen in the v3 API. Can anyone help me?
I have found a solution that I think that is "the official" for what you want to do.
First, you have to activate a Google API "Client ID for installed applications".
Go to the Google API console and create the project.
Then, activate the calendar.
Go to the "API access" option, and use the "Create OAuth 2.0 client" button.
Give a name (and a logo, if you want) to the product. Click "next".
Choose the "Installed application" option and click "Create Client Id".
Now you have your access configurated. Now, you will need some codes. To obtain them:
*The "Authentication Code". To get it, you need the following information:
SCOPE: https://www.google.com/calendar/feeds/ (if you want to access the calendar API. There are others you can find them at the OAuth 2.0 Playground)
CLIENT_ID: You will find it at the API Access Section at the Google API Console.
REDIRECT_URI: Get it at the same place.
Now, copy the following code into a file, put the values into the variables, execute the code (php -q script_name.php), and go to the URL printed.
<?php
$scope = '';
$client_id = '';
$redirect_uri = '';
$params = array(
'response_type' => 'code',
'client_id' => $client_id,
'redirect_uri' => $redirect_uri,
'scope' => $scope
);
$url = 'https://accounts.google.com/o/oauth2/auth?' . http_build_query($params);
echo $url."\n";
?>
The web page will ask you to allow the access. Do it, and you will get a code, which is your Authentication Code.
*The "Refresh Code". To get it, you will need:
The data you used before, plus the "client secret" code in the API Console, between the "client id" and the "redirect URI".
As you did before, copy the following code, and put the variables in place (the code field is the Authentication Code).
Execute and the result is the "Refresh Token".
<?php
$url = 'https://accounts.google.com/o/oauth2/token';
$post_data = array(
'code' => '',
'client_id' => '',
'client_secret' => '',
'redirect_uri' => '',
'grant_type' => 'authorization_code',
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$token = json_decode($result);
echo $token->refresh_token . "\n";
?>
At this moment, you have all you need. Be careful if one day you change the Authentication Code. You will have to get new keys.
To access a calendar service, here you have the example:
Change the variable values before using it.
This example gets the primary calendar events, but you can change the address for any in the calendar API (http://code.google.com/intl/ca/apis/calendar/v3/getting_started.html#background_operations)
<?php
$scope = 'https://www.google.com/calendar/feeds/';
$client_id = '';
$client_secret = '';
$redirect_uri = '';
$refresh_token = '';
$token_url = 'https://accounts.google.com/o/oauth2/token';
$post_data = array(
'client_secret' => $client_secret,
'grant_type' => 'refresh_token',
'refresh_token' => $refresh_token,
'client_id' => $client_id
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $token_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$token_object = json_decode($result);
$access_token = $token_object->access_token;
// Get the results
$rest_url = 'https://www.googleapis.com/calendar/v3/calendars/primary/events';
$header = "Authorization: OAuth " . $access_token;
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, array($header));
curl_setopt($ch, CURLOPT_URL, $rest_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$rest_result = curl_exec($ch);
print_r(json_decode($rest_result));
?>
First, the script asks for an "Access Token", which is valid for an hour. Then, the script gets the REST service (any in the calendar scope), sending the access token in the header.
To give a best speed at the scrip, it would be good to store the access token in a cache until it's older than 3600 seconds. This way, the script would avoid one of the two calls.
Tips:
Visit OAuth 2.0 Playground to understand all the information sent in the OAuth process. It helped me a lot
A post by Eric Nagel in his blog gave me the solution. All the merit is to him. I can't link it since I haven't got enough "reputation".
You will need to use both the Developer Key (API Key) and OAuth2. The developer key authenticates who wrote the software and is used for things like quota which is on a per developer basis not a per user basis. OAuth2 is for user authentication and will be need to access the non-public calendar.
OAuth2 has a renew token from which you can generate a session token and this means that you will not need to screen scrape the OAuth screens to get authenticated. To get this I would write a little command line application, or you use a one off PHP page.
Under the Google Api Console go to API Access
Generate a new Client ID and choose Installed Application ( as you will be authenticating you server as you not as your user)
Either using a console app or a one off PHP page authenticate using OAuth and your google account (the one with the calendar you want access to)
In the return from the authentication there should be a renew token, (called renew or refresh or something similar). Save this string and make it available to your PHP site.
When you need to access the service your OAuth library should have a renew/refresh call. There is an example using .Net below.
private IAuthorizationState CreateAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { AdsenseService.Scopes.AdsenseReadonly.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
if (refreshToken.IsNotNullOrEmpty()) // refreshToken you stored in step 4
{
try
{
state.RefreshToken = refreshToken;
if (arg.RefreshToken(state)) // This is calling out to the OAuth servers with the refresh token getting back a session token, returns true if successful.
{
if (state.RefreshToken != refreshToken) // if the refresh token has changed, save it.
{
PersistRefreshToken(authorization.RefreshToken);
}
return this.authorization = state; // Retain the authorization state, this is what will authenticate your calls.
}
}
catch (ProtocolException ex) {...}
The AuthorisationState that has now been renewed can then be used to authenticate call you make to the API. this state can be used many time until it expires and then can be refreshed. As you are authenticating your application as yourself not as a user this AuthorisationState can be shared by all you sessions. Both the current AuthorisationState and the refresh token should be kept securely on your server and never sent to the client, if you ever sent these as part of a response your clients would have the same privileges as your code application
Can also be used with the Google php library. The access token for the $client->setAccessToken() function has to be formatted in the following way:
$at= '{"access_token":"' . $access_token . '",' .
'"token_type":"Bearer",' .
'"expires_in":3600,' .
'"refresh_token":"' . $refresh_token . '",',
'"created":' . time() . '}';
Where $access_token is the access token found by you and $refresh_token is the refresh token. Tested with the useless simple.php google example.
Authentication is then just:
$client->setAccessToken($at);