Skip to content

Commit

Permalink
Add USOS OAuth1.0a provider (#972)
Browse files Browse the repository at this point in the history
Co-authored-by: atymic <[email protected]>
Co-authored-by: Władysław Mostowicz <[email protected]>
  • Loading branch information
3 people authored Feb 21, 2023
0 parents commit 55a8d00
Show file tree
Hide file tree
Showing 5 changed files with 319 additions and 0 deletions.
30 changes: 30 additions & 0 deletions Provider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace SocialiteProviders\Usos;

use SocialiteProviders\Manager\OAuth1\AbstractProvider;
use SocialiteProviders\Manager\OAuth1\User;

class Provider extends AbstractProvider
{
/**
* Unique Provider Identifier.
*/
public const IDENTIFIER = 'USOS';

protected function mapUserToObject(array $user)
{
return (new User())->setRaw($user['extra'])->map([
'id' => $user['id'],
'nickname' => $user['nickname'],
'name' => $user['name'],
'email' => $user['email'],
'avatar' => $user['avatar'],
]);
}

public static function additionalConfigKeys()
{
return ['profile_fields_selector', 'domain'];
}
}
65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Usos

```bash
composer require socialiteproviders/usos
```

## Installation & Basic Usage

Please see the [Base Installation Guide](https://socialiteproviders.com/usage/), then follow the provider specific instructions below.

### Add configuration to `config/services.php`

```php
'usos' => [
'domain' => env('USOS_DOMAIN_URL'), // Because of every instance of USOS is self-hosted
'client_id' => env('USOS_CLIENT_ID'),
'client_secret' => env('USOS_CLIENT_SECRET'),
'redirect' => env('USOS_REDIRECT_URI')
],
```

As a default, provider loads only necessary for Socialite data. If you need to get more data during authentication process, such as student status or phone number, you may add ```profile_fields_selector``` parameter:

```php
'usos' => [
// ...
'profile_fields_selector' => env('USOS_PROFILE_FIELDS_SELECTOR'),
// ...
]
```

Some fields may be not available without ```scopes``` field specifying.

### Add provider event listener

Configure the package's listener to listen for `SocialiteWasCalled` events.

Add the event to your `listen[]` array in `app/Providers/EventServiceProvider`. See the [Base Installation Guide](https://socialiteproviders.com/usage/) for detailed instructions.

```php
protected $listen = [
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
// ... other providers
'SocialiteProviders\\Usos\\UsosExtendSocialite@handle',
],
];
```

### Usage

You should now be able to use the provider like you would regularly use Socialite (assuming you have the facade installed):

```php
return Socialite::driver('usos')->redirect();
```

You also can use USOS scopes via Socialite ```scopes``` method:

```php
return Socialite::driver('usos')
->scopes(['personal', 'email'])
->redirect();
```

To get more information about ```scopes``` you may visit a documentation page at yours USOS instance, or at the [USOSapi Mother server webpage](https://apps.usos.edu.pl/developers/api/authorization/).
172 changes: 172 additions & 0 deletions Server.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<?php

namespace SocialiteProviders\Usos;

use GuzzleHttp\Exception\BadResponseException;
use League\OAuth1\Client\Credentials\CredentialsException;
use League\OAuth1\Client\Credentials\TemporaryCredentials;
use League\OAuth1\Client\Credentials\TokenCredentials;
use SocialiteProviders\Manager\OAuth1\Server as BaseServer;
use SocialiteProviders\Manager\OAuth1\User;

class Server extends BaseServer
{
/**
* {@inheritdoc}
*/
public function urlTemporaryCredentials()
{
return $this->getDomain().'/services/oauth/request_token';
}

/**
* {@inheritdoc}
*/
public function urlAuthorization()
{
return $this->getDomain().'/services/oauth/authorize';
}

/**
* {@inheritdoc}
*/
public function urlTokenCredentials()
{
return $this->getDomain().'/services/oauth/access_token';
}

/**
* {@inheritdoc}
*/
public function urlUserDetails()
{
return $this->getDomain().'/services/users/user'.'?fields='.$this->getFieldsSelector();
}

/*
* Returns an instance domain, according to the environment configuration
*/
private function getDomain()
{
return $this->getConfig('domain');
}

/*
* Returns a fields selector for services/users/user method, which defines a list of fields,
* which will be returned from API endpoint.
*/
private function getFieldsSelector()
{
return $this->getConfig('profile_fields_selector', 'id|first_name|last_name|email|photo_urls');
}

/**
* {@inheritdoc}
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
$user = new User();
$user->id = $data['id'];
$user->nickname = $data['first_name'].' '.$data['last_name'];
$user->name = $data['first_name'].' '.$data['last_name'];
$user->avatar = $data['photo_urls']['50x50'] ?? null;
$user->email = $data['email'] ?? null;

$user->extra = $data;

return $user;
}

/**
* Generate the OAuth protocol header for a temporary credentials
* request, based on the URI.
*
* @param string $uri
*
* @return string
*/
protected function temporaryCredentialsProtocolHeader($uri)
{
$parameters = array_merge($this->baseProtocolParameters(), array_merge([
'oauth_callback' => $this->clientCredentials->getCallbackUri(),

], ['scopes' => implode('|', $this->scopes)]));

$parameters['oauth_signature'] = $this->signature->sign($uri, $parameters, 'POST');

return $this->normalizeProtocolParameters($parameters);
}

public function getTemporaryCredentials()
{
$uri = $this->urlTemporaryCredentials();

$client = $this->createHttpClient();

$formParams = [
'scopes' => implode('|', $this->scopes),
];

$header = $this->temporaryCredentialsProtocolHeader($uri);
$authorizationHeader = ['Authorization' => $header];
$headers = $this->buildHttpClientHeaders($authorizationHeader);

try {
$response = $client->post($uri, [
'headers' => $headers,
'form_params' => $formParams,
]);

return $this->createTemporaryCredentials((string) $response->getBody());
} catch (BadResponseException $e) {
$this->handleTemporaryCredentialsBadResponse($e);
}

throw new CredentialsException('Failed to get temporary credentials');
}

/**
* {@inheritdoc}
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
return $data['id'];
}

/**
* {@inheritdoc}
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
return $data['email'] ?? null;
}

/**
* {@inheritdoc}
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
return $data['screen_name'] ?? null;
}

/**
* {@inheritdoc}
*/
public function getAuthorizationUrl($temporaryIdentifier, array $options = [])
{
// Somebody can pass through an instance of temporary
// credentials, and we'll extract the identifier from there.
if ($temporaryIdentifier instanceof TemporaryCredentials) {
$temporaryIdentifier = $temporaryIdentifier->getIdentifier();
}
$queryOauthToken = ['oauth_token' => $temporaryIdentifier];
$parameters = (isset($this->parameters))
? array_merge($queryOauthToken, $this->parameters)
: $queryOauthToken;

$url = $this->urlAuthorization();
$queryString = http_build_query($parameters);

return $this->buildUrl($url, $queryString);
}
}
18 changes: 18 additions & 0 deletions UsosExtendSocialite.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace SocialiteProviders\Usos;

use SocialiteProviders\Manager\SocialiteWasCalled;

class UsosExtendSocialite
{
/**
* Register the provider.
*
* @param SocialiteWasCalled $socialiteWasCalled
*/
public function handle(SocialiteWasCalled $socialiteWasCalled): void
{
$socialiteWasCalled->extendSocialite('usos', Provider::class, Server::class);
}
}
34 changes: 34 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "socialiteproviders/usos",
"description": "USOS OAuth1 Provider for Laravel Socialite",
"license": "MIT",
"keywords": [
"laravel",
"oauth",
"provider",
"socialite",
"USOS",
"Uniwersytecki System Obsługi Studiów"
],
"authors": [
{
"name": "Władysław Mostowicz",
"email": "[email protected]"
}
],
"support": {
"issues": "https://github.com/socialiteproviders/providers/issues",
"source": "https://github.com/socialiteproviders/providers",
"docs": "https://socialiteproviders.com/usos"
},
"require": {
"php": "^8.0",
"ext-json": "*",
"socialiteproviders/manager": "~4.0"
},
"autoload": {
"psr-4": {
"SocialiteProviders\\Usos\\": ""
}
}
}

0 comments on commit 55a8d00

Please sign in to comment.