Azure : Using PHP to go all oauth2 on the management API!

Introduction

As a hobby effort, I wanted to create a small poc where any user would be able to login with their AAD user, grant access to an application, after which that application could query their subscriptions.

In all honesty, I’ve been struggling more than I like to admit with getting this working… So this post will cover all the steps that you need to do to get this working!

 

Oauth & Azure AD

Before getting our hands dirty, read up on the following post ; Authorize access to web applications using OAuth 2.0 and Azure Active Directory

Ready it thoroughly! To be honest, I didn’t at first and it cost me a lot of time. 😉

Anyhow, the flow looks as follows…

active-directory-oauth-code-flow-native-app

So basically;

  • We’ll redirect the user to sign-in (and if this hasn’t been done, grant our application access)
  • If all went well, we’ll receive an authorization code
  • We’ll use this code to get a bearer (and refresh) token
  • Next up we’ll use the bearer code to connect to the Azure REST API for getting the list of subscriptions for that user.

 

Prep on Azure AD

First start by creating a web application on Azure Active Directory. Be sure to set your reply url correct… AND (important) add “Windows Azure Service Management” as an additional application. Grant the delegated permission too.

2016-10-21-20_34_15-active-directory-microsoft-azure

Also be sure to set the application to “multi-tenant”.

2016-10-21-20_37_03-active-directory-microsoft-azure

And then generate your key… Be sure to note this down.

2016-10-21-20_36_58-active-directory-microsoft-azure

Just like with the client id!

2016-10-21-20_38_17-active-directory-microsoft-azure

Important ; Be aware that the key will include the applications you have added. So… if you changed the application permissions, then you will need to generate a new key!

 

Code Sample – Curl

Below you can find a quick & dirty code sample with just using curl…

<?php
session_start();
error_reporting(-1);
ini_set('display_errors', 'On');

if (!isset($_GET['code'])) {
 $authUrl = "https://login.microsoftonline.com/common/oauth2/authorize?";
 $authUrl .= "client_id=your-client-id";
 $authUrl .= "&response_type=code";
 $authUrl .= "&redirect_uri=http%3A%2F%2Fauth.kvaes.be%2Findex.php";
 $authUrl .= "&response_mode=query";
 $authUrl .= "&resource=https%3A%2F%2Fmanagement.azure.com%2F";
 $authUrl .= "&state=12345";

 header('Location: '.$authUrl);
 exit;

} else {

 $accesscode = $_GET['code'];

 $ch = curl_init();
 curl_setopt($ch, CURLOPT_URL,"https://login.microsoftonline.com/common/oauth2/token");
 curl_setopt($ch, CURLOPT_POST, 1);
 $client_id = "your-client-id";
 $client_secret = "your-client-secret";
 curl_setopt($ch, CURLOPT_POSTFIELDS,
 "grant_type=authorization_code&client_id=".$client_id."&redirect_uri=http%3A%2F%2Fauth.kvaes.be%2Findex.php&resource=https%3A%2F%2Fmanagement.azure.com%2F&&code=".$accesscode."&client_secret=".urlencode($client_secret));
 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 $server_output = curl_exec ($ch);
 curl_close ($ch); 
 $jsonoutput = json_decode($server_output, true);

 $bearertoken = $jsonoutput['access_token'];
 $url = "https://management.azure.com/subscriptions/?api-version=2015-01-01";
 $ch = curl_init($url);
 $User_Agent = 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31';
 $request_headers = array();
 $request_headers[] = 'User-Agent: '. $User_Agent;
 $request_headers[] = 'Accept: application/json';
 $request_headers[] = 'Authorization: Bearer '. $bearertoken;
 curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
 $result = curl_exec($ch);
 curl_close($ch);
 echo $result;
 
}

?>

The most important things to note ;

  • We’ll be using “common” as tenant in the oauth authorize uri. This will ensure that this sample works for all tenants and not just a single one.
  • The redirect uri should map the one you configured in your application.
  • The resource should be included, where we’ll be using the “management.azure.com” to get a bearer token that applies to this target.
  • This code sample does not facilitate refresh tokens.

 

Code Sample – Oauth 2.0 Client

The following code sample is about the same, but this will leverage two libraries ;

 

<?php
session_start();
require __DIR__ . '/vendor/autoload.php';

error_reporting(-1);
ini_set('display_errors', 'On');

$provider = new TheNetworg\OAuth2\Client\Provider\Azure([
 'clientId' => 'your-client-id',
 'clientSecret' => 'your-client-secret',
 'redirectUri' => 'http://auth.kvaes.be/index.php',
 'resource' => 'https://management.azure.com/'
]);

if (!isset($_GET['code'])) {

 // If we don't have an authorization code then get one
 $authUrl = $provider->getAuthorizationUrl();
 $_SESSION['oauth2state'] = $provider->getState();
 header('Location: '.$authUrl);
 exit;

} else {

 // Try to get an access token (using the authorization code grant)
 $token = $provider->getAccessToken('authorization_code', [
 'code' => $_GET['code']
 ]);

 $accesstoken = $provider->getAccessToken('refresh_token', [
 'refresh_token' => $token->getRefreshToken(),
 'resource' => 'https://management.core.windows.net/'
 ]);

 $bearertoken = "Bearer " . $accesstoken->getToken();

 $client = new GuzzleHttp\Client([
 'base_uri' => 'https://management.azure.com/',
 'timeout' => 2.0,
 ]);

 try {
 $result = $client->request('GET', "/subscriptions/?api-version=2015-01-01", [
 'headers' => [
 'User-Agent' => 'testing/1.0',
 'Accept' => 'application/json',
 'Authorization' => $bearertoken
 ]
 ]);
 } catch (RequestException $e) {
 echo Psr7\str($e->getRequest());
 if ($e->hasResponse()) {
 echo Psr7\str($e->getResponse());
 }
 }
 echo $result->getBody();

}

?>

 

Most important things to note here ;

  • Be sure to add the resource to the provide that covers the Azure API
  • All remarks from previous code also apply here… 😉

 

TL;DR

  • The oauth approach can provide you with a nice approach to act on behalf of a user.
  • Access tokens have a limited lifetime. This can be configured on tenant level. Which will be a balance between security & usability.
  • The common endpoint is used when you are looking for a multi tenant approach.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s