Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
Signed-off-by: Frank Rochlitzer <f.rochlitzer@b3-it.de>
  • Loading branch information
theroch committed May 15, 2020
1 parent 3bcdf7e commit ca41258
Show file tree
Hide file tree
Showing 5 changed files with 475 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/build
/vendor
/.idea
composer.phar
composer.lock
117 changes: 115 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,115 @@
# oauth2-adfs
This package provides Microsoft ADFS OAuth 2.0 support for the PHP League's OAuth 2.0 Client.
# Microsoft ADFS Provider for OAuth 2.0 Client
[![Latest Version](https://img.shields.io/github/release/b3-it/oauth2-adfs.svg?style=flat-square)](https://github.com/b3-it/oauth2-adfs/releases)
[![Build Status](https://img.shields.io/travis/b3-it/oauth2-adfs/master.svg?style=flat-square)](https://travis-ci.org/b3-it/oauth2-adfs)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/b3-it/oauth2-adfs.svg?style=flat-square)](https://scrutinizer-ci.com/g/b3-it/oauth2-adfs/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/b3-it/oauth2-adfs.svg?style=flat-square)](https://scrutinizer-ci.com/g/b3-it/oauth2-adfs)
[![Total Downloads](https://img.shields.io/packagist/dt/b3-it/oauth2-adfs.svg?style=flat-square)](https://packagist.org/packages/b3-it/oauth2-adfs)
[![Software License](https://img.shields.io/packagist/l/b3-it/oauth2-adfs.svg?style=flat-square)](LICENSE.md)

This package provides Microsoft ADFS OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client).

## Installation

To install, use composer:

```
composer require b3-it/oauth2-adfs
```

## Usage

Usage is the same as The League's OAuth client, using `\B3it\OAuth2\Client\Provider\Adfs` as the provider.

### Authorization Code Flow

```php
$provider = new B3it\OAuth2\Client\Provider\Adfs([
// Required
'clientId' => '{microsoft-client-id}',
'clientSecret' => '{microsoft-client-secret}',
'authServerUrl' => 'https://my.ms-server.de/adfs',
// Optional
'redirectUri' => 'https://example.com/callback-url',
'resource' => 'https://example.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;

// Check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {

unset($_SESSION['oauth2state']);
exit('Invalid state');

} else {

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

// Optional: Now you have a token you can look up a users profile data
try {

// We got an access token, let's now get the user's details
$user = $provider->getResourceOwner($token);

// Use these details to create a new profile
printf('Hello %s!', $user->geName());

} catch (Exception $e) {

// Failed to get user details
exit('Oh dear...');
}

// Use this to interact with an API on the users behalf
echo $token->getToken();
}
```

#### Managing Scopes and State

When creating your Microsoft ADFS authorization URL, you can specify the state and scopes your application may authorize.

```php
$options = [
'scope' => ['allatclaims', 'openid'] // array or string
];

$authorizationUrl = $provider->getAuthorizationUrl($options);
```
If neither are defined, the provider will utilize internal defaults.

At the time of authoring this documentation, the following scopes are available.


##### Scopes
- logon_cert
- aza
- allatclaims
- email
- profile
- winhello_cert
- openid
- vpn_cert
- user_impersonation


## Credits

- [B3 IT Systeme GmbH](https://github.com/b3-it)
- [All Contributors](https://github.com/b3-it/oauth2-adfs/contributors)


## License

The MIT License (MIT). Please see [License File](https://github.com/b3-it/oauth2-adfs/blob/master/LICENSE) for more information.

41 changes: 41 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "b3-it/oauth2-adfs",
"version": "1.0.0.0",
"description": "Microsoft Active Directory Federation Service (ADFS) OAuth 2.0 Client Provider for The PHP League OAuth2-Client",
"license": "MIT",
"authors": [
{
"name": "Frank Rochlitzer",
"email": "f.rochlitzer@b3-it.de",
"homepage": "https://github.com/b3-it"
}
],
"keywords": [
"oauth",
"oauth2",
"client",
"authorization",
"authorisation",
"microsoft",
"adfs"
],
"require": {
"league/oauth2-client": "^2.0"
},
"require-dev": {
"roave/security-advisories": "dev-master",
"phpunit/phpunit": "~4.0",
"mockery/mockery": "~0.9",
"squizlabs/php_codesniffer": "~2.0"
},
"autoload": {
"psr-4": {
"B3it\\OAuth2\\Client\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"B3it\\OAuth2\\Client\\Test\\": "tests/src/"
}
}
}
189 changes: 189 additions & 0 deletions src/Provider/Adfs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php
namespace B3it\OAuth2\Client\Provider;

use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
use League\OAuth2\Client\Token\AccessToken;

class Adfs extends GenericProvider
{
private $responseResourceOwnerId = 'upn';

private $scopes = ['openid'];

protected $idToken = null;

protected $resource = null;

/**
* ADFS URL, eg. https://localhost/adfs.
*
* @var string
*/
public $authServerUrl = null;

public function __construct(array $options = [], array $collaborators = [])
{
if (isset($options['idToken'])) {
$token = $options['idToken'];
if ($token instanceof AccessToken) {
$options['idToken'] = $this->getIdTokenFromAccessToken($token);
}
}
parent::__construct($options, $collaborators);
}

/**
* Get authorization url to begin OAuth flow
*
* @return string
*/
public function getBaseAuthorizationUrl()
{
return $this->getBaseUrl().'/oauth2/authorize/';
}

/**
* Get access token url to retrieve token
*
* @param array $params
*
* @return string
*/
public function getBaseAccessTokenUrl(array $params)
{
return $this->getBaseUrl().'/oauth2/token/';
}

/**
* Builds the logout URL.
*
* @param array $options
* @return string Authorization URL
*/
public function getLogoutUrl(array $options = [])
{
$base = $this->getBaseLogoutUrl();
$params = $this->getAuthorizationParameters($options);
if (isset($params['redirect_uri'])) {
$params['post_logout_redirect_uri'] = $params['redirect_uri'];
unset($params['redirect_uri']);
if ($this->idToken) {
$params['id_token_hint'] = $this->idToken;
}
}
$query = $this->getAuthorizationQuery($params);
return $this->appendQuery($base, $query);
}

public function getAccessToken($grant, array $options = [])
{
$token = parent::getAccessToken($grant, $options);

//We have to preserve the id_token for id_token_hint in the logout url
$this->idToken = $this->getIdTokenFromAccessToken($token);
return $token;
}

/**
* Requests and returns the resource owner of given access token.
*
* @param AccessToken $token
* @return ResourceOwnerInterface
*/
public function getResourceOwner(AccessToken $token)
{
$response = [];

return $this->createResourceOwner($response, $token);
}

public function getIdTokenFromAccessToken($token) {
if (!($token instanceof AccessToken)) {
return null;
}

$values = $token->getValues();
return $values['id_token'] ?? null;
}

public function getIdToken() {
return $this->idToken;
}

public function getDefaultScopes()
{
$defaults = parent::getDefaultScopes();

if (is_array($defaults)) {
return array_merge($defaults, $this->scopes);
}
return $this->scopes;
}

/**
* Get logout url to logout of session token
*
* @return string
*/
private function getBaseLogoutUrl()
{
return $this->getBaseUrl() . '/oauth2/logout';
}

/**
* Returns all options that are required.
*
* @return array
*/
protected function getRequiredOptions()
{
return [
'authServerUrl',
];
}

/**
* Creates base url from provider configuration.
*
* @return string
*/
protected function getBaseUrl()
{
return trim($this->authServerUrl, " \t\n\r\0\x0B/");
}

/**
* @inheritdoc
*/
protected function createResourceOwner(array $response, AccessToken $token)
{
return new AdfsResourceOwner($token, $response, $this->responseResourceOwnerId);
}

/**
* Returns the list of options that can be passed to the HttpClient
*
* @param array $options An array of options to set on this provider.
* Options include `clientId`, `clientSecret`, `redirectUri`, and `state`.
* Individual providers may introduce more options, as needed.
* @return array The options to pass to the HttpClient constructor
*/
protected function getAllowedClientOptions(array $options)
{
$client_options = ['timeout', 'proxy', 'verify'];

return $client_options;
}

protected function getAuthorizationParameters(array $options)
{
$params = parent::getAuthorizationParameters($options);

if ($this->resource) {
$params['resource'] = $this->resource;
}

return $params;
}
}
Loading

0 comments on commit ca41258

Please sign in to comment.