spring-authorization-server icon indicating copy to clipboard operation
spring-authorization-server copied to clipboard

Provide more flexibility on when to display consent page

Open petrdvorak opened this issue 1 year ago • 11 comments

Expected Behavior

When logging in via OAuth 2.1 dance, the consent is only requested once. For more security/compliance-sensitive applications, it would be nice to have a simple way to enforce the consent screen on every login so that the consent is re-granted.

We would like to be able to configure, i.e.:

final RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                // ... 
                // Consent.ALWAYS / Consent.ONCE / Consent.NEVER
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(Consent.ALWAYS).build())
                .build();

Current Behavior

Once consent is granted to given scopes, the consent screen is not shown.

We are able to configure:

final RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                // ...
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

Context

We are mostly trying to have a consistent user flow, where the sequence of screens is the same for each login. The additional reason is that we would like the user to re-confirm the consent granted to the third-party app.

Related gh-1363

petrdvorak avatar Feb 20 '24 01:02 petrdvorak

I've been eager to contribute for some time now and this looks like something I can tackle.

@jgrandja If this is something that you want to support, I will gladly work on it.

MrJovanovic13 avatar Feb 22 '24 02:02 MrJovanovic13

@petrdvorak

When logging in via OAuth 2.1 dance, the consent is only requested once. For more security/compliance-sensitive applications, it would be nice to have a simple way to enforce the consent screen on every login so that the consent is re-granted.

I believe you are looking for support for the OIDC parameter prompt=consent. Please see this comment. Can you confirm?

Do you also need similar support for the standard OAuth 2.0 authorization_code grant flow? Or you just need it for OIDC (login) flow?

jgrandja avatar Feb 26 '24 16:02 jgrandja

Thank you for your interest in contributing @MrJovanovic13.

I will reach out to you as soon as we figure out the scope of work and have it scheduled for a release 👍

jgrandja avatar Feb 26 '24 16:02 jgrandja

@jgrandja This does not seem to be what I am looking for, as this seems to be the client-side control only. (... or, am I missing how this could actually ensure the consent was granted again? Removing the parameter would skip the consent screen, so I do not see why this is even in the specification...)

The intended behavior is more flexibility on when to display consent. With the old Spring OAuth 2.0 support, we actually used to have a fully dynamic configuration of when the consent form is shown based on our custom logic. Therefore, we were able to do things such as:

    @Override
    public InitConsentFormResponse initConsentForm(String userId, String organizationId, OperationContext operationContext) {

        final ApplicationContext applicationContext = operationContext.getApplicationContext();
        final String clientId = applicationContext.getId();
        final String operationName = operationContext.getName();

        // Consent is always required for login_sca operation
        if ("login_sca".equals(operationName)) {
            return new InitConsentFormResponse(true);
        }

        final List<String> requestedScopes = applicationContext.getOriginalScopes();
        final String consentId;
        if (requestedScopes.contains("aisp")) {
            consentId = "aisp";
        } else if (requestedScopes.contains("pisp")) {
            consentId = "pisp";
        } else {
            consentId = "profile";
        }

        final boolean consentActive = isConsentActive(userId, consentId, clientId, consentExpirationTimestamp);
        return new InitConsentFormResponse(!consentActive);
    }

... so, always require consent for login, and sometimes - i.e., every 180 days - for other operations, such as payment or XS2A (access to an account).

petrdvorak avatar Feb 26 '24 16:02 petrdvorak

@petrdvorak Thanks for the code sample.

It looks like you need a hook into OAuth2AuthorizationCodeRequestAuthenticationProvider.requireAuthorizationConsent() (and access to OAuth2AuthorizationCodeRequestAuthenticationToken) so you can evaluate at runtime?

jgrandja avatar Feb 26 '24 18:02 jgrandja

@jgrandja Yes, this would be ideal.

petrdvorak avatar Feb 27 '24 07:02 petrdvorak

nice addition.

rishiraj88 avatar Feb 27 '24 07:02 rishiraj88

@petrdvorak Looks like this customization capability will be useful for our users. We'll look at adding an enhancement as per comment.

jgrandja avatar Feb 27 '24 15:02 jgrandja

@MrJovanovic13 We now know the scope of work as per comment. Let me know if this is still something you would like to take on.

jgrandja avatar Feb 27 '24 15:02 jgrandja

@jgrandja Yes, I would still like to take this on.

MrJovanovic13 avatar Feb 28 '24 11:02 MrJovanovic13

@MrJovanovic13 Great, I've assigned it to you.

I think what we need to add is something along the lines of:

OAuth2AuthorizationCodeRequestAuthenticationProvider.setRequiresAuthorizationConsent(Predicate<OAuth2AuthorizationCodeRequestAuthenticationContext> requiresAuthorizationConsent)

This pattern is used in the following implementations:

OAuth2AuthorizationCodeRequestAuthenticationProvider.setAuthenticationValidator()
OAuth2AuthorizationConsentAuthenticationProvider.setAuthorizationConsentCustomizer()
OidcUserInfoAuthenticationProvider.setUserInfoMapper()
OAuth2ClientCredentialsAuthenticationProvider.setAuthenticationValidator()

Please review those implementations to become familiar with the pattern as I believe the same pattern will work here as well.

Thanks!

jgrandja avatar Feb 28 '24 16:02 jgrandja