Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More Openid connect work #25

Merged
merged 5 commits into from
Sep 4, 2019
Merged

Conversation

sigmunau
Copy link
Contributor

This pull request improves upon my previous pull request in the following ways:

  • It adds support for front channel logout (tested with azure ad)
  • It fixes id token verification for OPs that rotate signing keys often (google)
  • It simplifies configuration (at the expense of requiring more network traffic during auth)

@jaimeperez can you review?

Copy link

@jaimeperez jaimeperez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi!

I can't comment much on the OIDC-specific details as that's a bit over my head, but I've left a few comments in the code 😄

if (!$endSessionEndpoint) {
Logger::debug("authoauth2: $providerLabel No urlEndSession configured, not doing anything for logout");
Logger::debug("authoauth2: $providerLabel OP does not provided and 'end_session_endpoint', not doing anything for logout");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be reworded, I think.

Logger::debug("authoauth2: $providerLabel OP does not provide an 'end_session_endpoint', not doing anything for logout");

maybe?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed.

$this->handleRequestFromRequest($_REQUEST);
}

public function handleRequestFromRequest(array $request)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? If we need the additional level of indirection, maybe handleRequestFromRequest() should be private?

If the reason for the extra method is to have a default, I would do that in the handleRequest() method. We cannot use a variable as a default value for a function argument, but we can default it to null and capture that in the code:

    public function handleRequest(array $request = null)
    {
        $request = $request ?: $_REQUEST;

        Logger::debug('authoauth2: logout request=' . var_export($request, true));
        $config = \SimpleSAML\Configuration::getInstance();
        $sourceId = $request['authSource'];
        $as = new \SimpleSAML\Auth\Simple($sourceId);
        $as->logout([
            'oidc:localLogout' => true,
            'ReturnTo' => $config->getBasePath().'logout.php',
        ]);
    }

It might be worth also to investigate the possibility of using Symfony\Component\HttpFoundation\Request objects instead of the array scalar type for the argument, although this depends heavily on the module and Patrick should have a better overview. The symfony/http-foundation package has been available as a dependency starting on SSP 1.17.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason is to make it easier to write unit tests. Also, this is the way it was done for existing, similar code in the module.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhm, I see... But wouldn't my suggestion make it possible to test as well? Then you have two options: build a request and pass it as an argument to handleRequest(), or take that same request and assign it to $_REQUEST.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it would. I was just following existing code patterns in the module.


protected function getScopeSeparator()
{
return ' ';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be done with a class constant?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implements an interface from the upstream oauth2-client library. We could of course define a class constant and return that here, but I don't see the point of that. To replace the entire function with a constant would require api changes to the library we are using

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's an external library, nevermind. Just not worth it.


protected function getDefaultScopes()
{
return 'openid profile';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this. If this is not ever going to change, I think it makes sense to have it as a constant in the class, so that we reduce the amount of methods and make the values available to anybody using the class, if that ever makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same response as above

return $config;
}

public static function base64url_decode($input) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this makes sense as a public method. It doesn't feel like something this class should offer as an interface. If it's only intended for internal use, I'd suggest making it private or at least protected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair point.

$n = self::base64url_decode($key['n']);
$keys[$kid] = \RobRichards\XMLSecLibs\XMLSecurityKey::convertRSA($n, $e);
} else {
error_log("Failed to load key data for key id: " . $kid);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging should be done through SimpleSAML\Logger interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, copied this from the cli script a bit too quickly.

*/
public function getBaseAuthorizationUrl()
{
return $this->getOpenIDConfiguration()["authorization_endpoint"];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a little bit weird that we need to go through getOpenIDConfiguration() every time we need to do something with the configuration. Would it be an option to initialize the $openIdConfiguration property in the constructor, and then refer always to $this->openIdConfiguration instead? Are there any other entrypoints to the class apart from the constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getOpenIDConfiguration() does a http fetch. I personally does not like to do IO in constructors. It makes for ugly code, and also I think it might be possible to use this class without requiring that configuration in some cases and then it would be a waste of resources to do the network IO in the constructor.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. If it's possible to use this class in some way without ever loading a OIDC configuration from the OP, then it makes sense to keep it like it is 👍

*/
public function getBaseAccessTokenUrl(array $params)
{
return $this->getOpenIDConfiguration()["token_endpoint"];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also a bit weird that $params here and $token in the next one aren't even used. What happens if token_endpoint is missing from the configuration, btw? Is that even possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These parameters are also defined in the oauth2-client library's api. No idea what they are supposed to be used for, but we can't just remove them here. As for missing the endpoint in the configuration, that would require a OP that doesn't comply with the spec. We should probably check that we have a minimum required config in getOpenODConfiguration() and give some sort of sensible feedback to the user if not.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a sensible idea for the getOpenIDConfiguration() method, indeed! At least, we should not take things for granted, just in case.

@sigmunau sigmunau force-pushed the openid-connect branch 2 times, most recently from 0fc8a37 to dbdc03d Compare August 30, 2019 12:42
This replaces the script that generates authsource config with just
the need to place the issuer url in the authsource config. It turns
out that some of the openid connect providers out there cycles the
signing keys so often that storing them directly in the config isn't a
good idea.

This commit also moves most of the OpenID Connect specific code into a
new Provider class, that could in theory also be used by other users
of the oauth2-client library
@sigmunau
Copy link
Contributor Author

sigmunau commented Sep 3, 2019

Request updated with sanity checks on getOpenIDConfiguration and style guide fixes

@pradtke pradtke merged commit 98949b3 into cirrusidentity:master Sep 4, 2019
@pradtke
Copy link
Contributor

pradtke commented Sep 4, 2019

Thank you @sigmunau

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants