spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.75k stars 5.88k forks source link

Support prompt in OIDC flows #6815

Open hauntingEcho opened 5 years ago

hauntingEcho commented 5 years ago

Summary

Some workflows in OpenID Connect are dependent on being able to use prompt=none to asynchronously refresh the session in the background. In particular, the draft specs for session management depend on the ability to use prompt=none via requests in a hidden iframe (see section 4.1). This is analogous to using isPassive in a SAML2 AuthnRequest.

In order to do that in a web application using spring-security, the OAuth2AuthorizationRequestResolver in use needs to be able to send the prompt=none flag when redirecting a user to an upstream identity provider when non-interactivity is requested by a user. This would also imply that a mechanism would be needed by which clients can request non-interactive session refreshes (which could theoretically share behavior between OIDC & other backing systems such as SAML)

Currently, there is no support for this in DefaultOAuth2AuthorizationRequestResolver, which is used by default when configuring an OpenID Connect login flow. See also some discussion of this in #6742

Configuration

in the WebSecurityConfigurerAdapter:

// this actually comes from Springboot properties:
String loginPage = "/oauth2/authorization/wso2";
// ...
http
    .addFilterAfter(ajaxTimeoutRedirectFilter, ExceptionTranslationFilter.class) // return 401 when attampting unauthenticated AJAX
    .logout().logoutSuccessHandler(oidcLogoutRedirectHandler()).permitAll()  // should go away with support in spring-security 5.2
    .and()
    .authorizeRequests()
      .antMatchers("/censoredPage1.html").hasRole("role1")
      .antMatchers("/censoredPage2.html").hasRole("role2")
      .anyRequest().authenticated() // functional controllers responsible for their own authorization
    .and()
    .oauth2Login()
      .loginPage(loginPage) // only one identity provider should be configured - just go directly there
      .userInfoEndpoint().oidcUserService(userService) // add internal numeric user ID which maps to incoming subject
    .and().and()
    .sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry) // allows session-killing to refresh authorization grants when they change
    .expiredSessionStrategy(new CallFailureSessionInformationExpiredStrategy(loginPage)); // on expired sessions: redirect synchronous requests, 401 async requests

in Spring-boot properties:

# NOTE: you need these two, but they're given by the OIDC provider (WSO2 Identity Server)
# spring.security.oauth2.client.registration.wso2.client-id =
# spring.security.oauth2.client.registration.wso2.client-secret =
# spring.security.oauth2.client.registration.wso2.client-name =
spring.security.oauth2.client.registration.wso2.provider=wso2
# Note well:  The scope list MUST be separated by commas.  There are multiple uses for this and some will work
# if you use spaces but others will fail in significant ways (like bypassing OIDC support and using OAuth2).
spring.security.oauth2.client.registration.wso2.scope=openid,email,phone
spring.security.oauth2.client.registration.wso2.redirect-uri-template=${my_url}/login/oauth2/code/wso2
spring.security.oauth2.client.registration.wso2.client-authentication-method=basic
spring.security.oauth2.client.registration.wso2.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.wso2.authorization-uri=${wso2.oauth2-root}/authorize
spring.security.oauth2.client.provider.wso2.token-uri=${wso2.issuer}
spring.security.oauth2.client.provider.wso2.user-info-uri=${wso2.oauth2-root}/userinfo
spring.security.oauth2.client.provider.wso2.jwk-set-uri=${wso2.oauth2-root}/jwks
spring.security.oauth2.client.provider.wso2.user-name-attribute=sub

server.port = 8443
my_hostname = https://localhost
my_url = ${my_hostname}:${server.port}
wso2.baseUrl = ${my_hostname}:9443
wso2.oauth2-root = ${wso2.baseUrl}/oauth2
wso2.issuer = ${wso2.oauth2-root}/token

Version

5.1.5 (via spring-boot 2.1.4)

ibaskine commented 5 years ago

Actually it shall be possible to configure any valid value for prompt. They are there for reason. I, for example, need to set it to "login"

jgrandja commented 5 years ago

@hauntingEcho @ibaskine Since Spring Security 5.1, you do have the ability to customize the Authorization Request by including additional parameter(s) (e.g. prompt) using a custom implementation of OAuth2AuthorizationRequestResolver. Please see the ref doc on how to do this.

Either way, I'm going to leave this ticket open as it addresses support for using the prompt parameter to asynchronously refresh the session in the background as outlined in the session management spec.

dominik-kovacs commented 8 months ago

@jgrandja @hauntingEcho I have a requirement to keep the user logged in until the application session expires. However, the identity provider's access token is valid for 10 hours, therefore the user is logged out after 10 hours while the application session is still active. Could this prompt feature potentially solve my problem?