This is a generic module for authentication against an OAuth2 or OIDC server. It performs the authorization_code
flow
and then uses the resulting access token to query an endpoint to get the user's attributes. It is a wrapper around the
excellent PHP League OAuth2 Client.
Compatability work for SSP2 will happen in the ssp2 branch.
If you are interested in using SSP as an OIDC OP see the OIDC module.
Table of Contents generated with DocToc
The module can be installed with composer.
composer require cirrusidentity/simplesamlphp-module-authoauth2
If you are on SSP 2 use version 4. If you are on SSP 1.X use version 3.
Or you can install the latest from master
composer require cirrusidentity/simplesamlphp-module-authoauth2:dev-master
If you install into a tar ball distribution of SSP then composer, by default, will also install all dev
dependencies for SSP and this module. This can be a long list.
If you prefer not having dev dependencies installed, then you can use.
composer require --no-update cirrusidentity/simplesamlphp-module-authoauth2 && composer update --no-dev cirrusidentity/simplesamlphp-module-authoauth2
The generic OAuth2 client is configured with
- the OAuth2 server endpoints
- the client secret/id
- optional parameters for scope
- optional query parameters for the authorization url
Almost all OAuth2/OIDC providers will require you to register a redirect URI. Use a url of the form below, and set hostname, SSP_PATH and optionally port to the correct values.
https://hostname/SSP_PATH/module.php/authoauth2/linkback.php
The majority of configuration items are passed through to the provider
implementation so it will depend on
the provider you choose. If you use the authsource authoauth2:OAuth2
without overriding the providerClass
then
the options from "Generic Usage" will work. If you use authoauth2:OpenIDConnect
then a different set of
configuration options are available.
scope vs scopes: The default provider used by authoauth2:OAuth2
supports setting 'scopes' => ['email']
or by setting
scope
in urlAuthorizeOptions
. Other providers may only support the latter (setting in urlAuthorizeOptions
).
Generic usage provides enough configuration parameters to use with any OAuth2 or OIDC server.
'oauth2' => array(
'authoauth2:OAuth2',
// *** Required for all integrations ***
'urlAuthorize' => 'https://www.example.com/oauth2/authorize',
'urlAccessToken' => 'https://www.example.com/oauth2/token',
'urlResourceOwnerDetails' => 'https://api.example.com/userinfo',
// You can add query params directly to urlResourceOwnerDetails or use urlResourceOwnerOptions.
'urlResourceOwnerOptions' => [
'fields' => 'id,name,first_name,last_name,email'
],
// allow fields from token response to be query params on resource owner details request
'tokenFieldsToUserDetailsUrl' => [
'fieldName' => 'queryParamName',
'access_token' => 'access_token',
'user_id' > 'user_id',
],
// *** Required for most integrations ***
// Test App.
'clientId' => '133972730583345',
'clientSecret' => '36aefb235314bad5df075363b79cbbcd',
// *** Optional ***
// Custom query parameters to add to authorize request
'urlAuthorizeOptions' => [
'prompt' => 'always',
// The underlying OAuth2 library also supports overriding requested scopes
//'scope' => ['other']
],
// Default scopes to request
'scopes' => ['email', 'profile'],
'scopeSeparator' => ' ',
// Customize redirect, if you don't want to use the standard /module.php/authoauth2/linkback.php
'redirectUri' => 'https://myapp.example.com/callback',
// See League\OAuth2\Client\Provider\GenericProvider for more options
// Guzzle HTTP config
// Wait up to 3.4 seconds for Oauth2 servers to respond
//http://docs.guzzlephp.org/en/stable/request-options.html#timeout
'timeout' => 3.4,
// http://docs.guzzlephp.org/en/stable/request-options.html#proxy
'proxy' => [
],
// All attribute keys will have this prefix
'attributePrefix' => 'someprefix.',
// Enable logging of request/response. This *will* leak you client secret and tokens into the logs
'logHttpTraffic' => true, //default is false
'logMessageFormat' => 'A Guzzle MessageFormatter format string', // default setting is sufficient for most debugging
'logIdTokenJson' => true, //default false. Log the json in the ID token.
),
For providers that support OpenID Connect discovery protocol the configuration can be simplified a bit. Only the issuer url, client id and client secret are required..
Not all configuration options from authoauth2:OAuth2
are supported in OpenIDConnect
'openidconnect' => array(
'authoauth2:OpenIDConnect',
// *** Required for all integrations ***
'issuer' => 'https://www.example.com', # e.g https://accounts.google.com
'clientId' => '133972730583345',
'clientSecret' => '36aefb235314bad5df075363b79cbbcd',
// Most Optional settings for OAuth2 above can be used
// *** Optional ***
// Customize post logout redirect, if you don't want to use the standard /module.php/authoauth2/loggedout.php
'postLogoutRedirectUri' => 'https://myapp.example.com/loggedout'
// Set a specific discovery url. Default is $issuer/.well-known/openid-configuration
'discoveryUrl' => 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
// Check if the issuer in the ID token matches the one from discovery. Default true. For some multi-tenant
// applications (for example cross tenant Azure logins) the token issuer varies with tenant
'validateIssuer' => false,
// Earlier version OpenIDConnect authsource doesn't support using `scopes` for overriding scope
//'urlAuthorizeOptions' => [
// 'scope' => 'openid'
//]
'ignoreEndSessionEndpoint' => false,
),
If your OP supports front channel single logout, you can configure https://hostname/SSP_PATH/module.php/authoauth2/logout.php?authSource=AUTHSOURCE
where AUTHSOURCE
is the id of your authsource in the authsources configuration (openidconnect
in the example above)
There are numerous Official and Third-Party providers that you can use instead of the generic OAuth2 provider. Using one of those providers can simplify the configurations.
To use a provider you must first install it composer require league/oauth2-some-provider
and then configure it.
'providerExample' => array(
// Must install correct provider with: composer require league/oauth2-example
'authoauth2:OAuth2',
'providerClass' => 'League\OAuth2\Client\Provider\SomeProvider',
'clientId' => 'my_id',
'clientSecret' => 'my_secret',
),
For some OAuth2 providers the generic endpoint configurations are already defined in ConfigTemplate
. You can reference
this to reduce the amount of typing needed in your authsource
'google' => array_merge(\SimpleSAML\Module\authoauth2\ConfigTemplate::GoogleOIDC, [
'clientId' => '3473456456-qqh0n4b5rrt7vkapgk3e4osre40.apps.googleusercontent.com',
'clientSecret' => 'shhh',
'urlAuthorizeOptions' => [
'hd' => 'example.com',
],
]),
or by using the template option
'providerExample' => array(
'authoauth2:OAuth2',
'template' => 'GoogleOIDC',
'clientId' => 'my_id',
'clientSecret' => 'my_secret',
),
Several of these samples show how to configure the generic endpoint to authenticate against Facebook, Amazon and Google, etc.
In a lot of cases you can use a template from ConfigTemplate
to make the configuration cleaner or you can use a provider specific implementations of the base OAuth2 client.
You can use the Facebook template 'template' => 'Facebook',
and then provide just the clientId
and clientSecret
to
have a cleaner looking config
'templateFacebook' => [
'authoauth2:OAuth2',
'template' => 'Facebook',
'clientId' => '13397273example',
'clientSecret' => '36aefb235314baexample',
],
'genericFacebookTest' => array(
'authoauth2:OAuth2',
// *** Facebook endpoints ***
'urlAuthorize' => 'https://www.facebook.com/dialog/oauth',
'urlAccessToken' => 'https://graph.facebook.com/oauth/access_token',
// Add requested attributes as fields
'urlResourceOwnerDetails' => 'https://graph.facebook.com/me?fields=id,name,first_name,last_name,email',
// *** My application ***
'clientId' => '13397273example',
'clientSecret' => '36aefb235314baexample',
'scopes' => 'email',
// *** Optional ***
// Custom query parameters to add to authorize request
'urlAuthorizeOptions' => [
// Force use to reauthenticate
'auth_type' => 'reauthenticate',
],
),
'genericAmazonTest' => array(
'authoauth2:OAuth2',
// *** Amazon Endpoints ***
'urlAuthorize' => 'https://www.amazon.com/ap/oa',
'urlAccessToken' => 'https://api.amazon.com/auth/o2/token',
'urlResourceOwnerDetails' => 'https://api.amazon.com/user/profile',
// *** My application ***
'clientId' => 'amzn1.application-oa2-client.94d04152358dexample',
'clientSecret' => '8681bdd290df87example',
'scopes' => 'profile',
),
View full Google instructions.
'genericGoogleTest' => array(
'authoauth2:OAuth2',
// *** Google Endpoints ***
'urlAuthorize' => 'https://accounts.google.com/o/oauth2/auth',
'urlAccessToken' => 'https://accounts.google.com/o/oauth2/token',
'urlResourceOwnerDetails' => 'https://www.googleapis.com/oauth2/v3/userinfo',
// *** My application ***
'clientId' => '685947170891-exmaple.apps.googleusercontent.com',
'clientSecret' => 'wV0FdFs_example',
'scopes' => array(
'openid',
'email',
'profile'
),
'scopeSeparator' => ' ',
),
View full Google instructions.
'googleProvider' => array(
// Must install correct provider with: composer require league/oauth2-google
'authoauth2:OAuth2',
'providerClass' => 'League\OAuth2\Client\Provider\Google',
'clientId' => 'client_id',
'clientSecret' => 'secret',
),
You can enable http logging with the logHttpTraffic
flag and optionally customize the
format with logMessageFormat
. See Guzzle's MessageFormatter
class for how to define a
format string.
security note: enabling http logging will make your client secret and any returned access or id tokens to appear in your logs.
You can use curl
on the command line to interact with the OAuth2/OIDC server.
Get Code
The first step is get a valid code AND ensure the module doesn't try to use it. This can be done by constructing an authorize url with an invalid state value but with all other params correct
If you log in with the above it will redirect you back to your SSP instance with an authorization_code
(which you should capture)
and then your SSP instance will report an error about an invalid state parameter. The state check happens prior to consuming the authorization_code
so you know SSP hasn't used it.
Get Access Token
Now you can use the code and try to get an access token
curl -d "code=REPLACE" \
-d "client_secret=my_secret" \
-d "client_id=my_client" \
-d "redirect_uri=https://myapp.example.com/simplesaml/module.php/authoauth2/linkback.php" \
-d 'grant_type=authorization_code' \
https://as.example.com/openid/token
Get User Info
Take the access token from above and call the user info endpoint
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://as.example.com/userInfo
If you are migrating away from an existing auth module, such as authfacebook
you will need to one of the following:
- add this module's
authoauth2
redirect URI to the facebook app, or - override the
authoauth2
authsource's redirect URI to match theauthfacebook
uri (https://myserver.com/module.php/authfacebook/linkback.php)
AND do one of the following- edit
/modules/authfacebook/www/linkback.php
to conditionally callOAuth2ResponseHandler
(see below) - configure an Apache rewrite rule to change '/module.php/authfacebook/linkback.php' to '/module.php/authoauth2/linkback.php'
- symlink or edit
/modules/authfacebook/www/linkback.php
to invoke the/modules/authoauth2/public/linkback.php
- edit
Some social providers support multiple login protocols and older SSP modules may use the non-OAuth2 version for login. To migrate to this module you may need to make some application changes. For example
- the LinkedIn module uses oauth1 and you may need to make adjustments to your app to move to their oauth2 API
- moving from OpenID 2 Yahoo logins to OIDC Yahoo logins requires creating a Yahoo app.
To migrate from an existing module you can adjust that modules linkback
/redirect handler to conditionally use the OAuth2ResponseHandler
In the below example the code will use the authoauth2
if that is what initiated the process else use the existing handler
$handler = new \SimpleSAML\Module\authoauth2\OAuth2ResponseHandler();
if ($handler->canHandleResponse()) {
$handler->handleResponse();
return;
}
The preprodwarning
module is include for testing authproc filters. note: The 1.0.2 version
of preprodwarning
has a bug in the redirect url. If using it you need to change showwarning.php
to warning
in your browser url.
docker run --name ssp-oauth2-dev \
--mount type=bind,source="$(pwd)",target=/var/simplesamlphp/staging-modules/authoauth2,readonly \
-e STAGINGCOMPOSERREPOS=authoauth2 \
-e COMPOSER_REQUIRE="cirrusidentity/simplesamlphp-module-authoauth2:@dev simplesamlphp/simplesamlphp-module-preprodwarning" \
-e SSP_ADMIN_PASSWORD=secret1 \
-e SSP_ENABLED_MODULES="authoauth2 preprodwarning" \
--mount type=bind,source="$(pwd)/docker/config/authsources.php",target=/var/simplesamlphp/config/authsources.php,readonly \
--mount type=bind,source="$(pwd)/docker/config/config-override.php",target=/var/simplesamlphp/config/config-override.php,readonly \
-p 443:443 cirrusid/simplesamlphp:v2.0.7
and visit (which resolves to localhost, and the docker container) the test authsource page to test some pre-configured social integrations (yes, you can see the app passwords, these apps are only used for this demo).
The pre-configured Facebook apps can only be accessed with a test account. You must be signed out of Facebook, otherwise you will get an error saying the application is not active.
- email: open_nzwvghb_user@tfbnw.net
- password: SSPisMyFavorite2022
Run phpcs
to check code style
php vendor/bin/phpcs