Spring Security OAuth2 AngularJS | Exit stream

Referring to the logout stream in the oauth2 spring-guides project, as soon as the user has authenticated with a user / password for the first time, credentials are not requested the next time after logging out.

How can I guarantee that the username / password is set every time after logging out.

This is what I am trying to implement: -

  • OAuth2 server issuing a JWT token using grant type "authorization_code" with automatic approval. This takes the form of html / angularjs to collect username / password.

  • UI / Webfront - uses @EnableSSO. ALL of its endpoints are authenticated, that is, it does not have an unauthorized landing page / ui / link that the user clicks to go to the / uaa server. So, hitting http: // localhost: 8080 instantly redirects you to http: // localhost: 9999 / uaa and presents a user form for collecting username / password.

  • Resource Server - uses @EnableResourceServer. Plain and simple REST api.

Using the above approach, I cannot train the output stream. HTTP POST / exit from the UI application clears the session / authorization in the user interface application, but users are automatically logged on to the network (since I chose automatic approval for all areas) without asking for the user password again.

Looking at the logs and calls of the networks, it seems that all the "oauth dance" happens again and again, without asking for the username / password again, and it looks like the auth server remembers the last authentication token issued to the client (using org.springframework.security.oauth2 .provider.code.InMemoryAuthorizationCodeServices?).

How can I tell the auth server to request a username / password each time it is requested for a code / token - is idle.

Or what is the best way to implement a way out of my script.

(To get a little closer to my requirements, remove the part permitAll()from UiApplication and configure autoApprovalthe aforementioned boot project on the auth server.)

github problem

+4
source share
3

, , Spring OAuth2 Single Sign Off. , .

  • .
  • , ( , cookie...) .

.

,

( ) WebSecurityConfig

...
@Value("${auth-server}/ssoLogout")
private String logoutUrl;
@Autowired
private CustomLogoutHandler logoutHandler;
...
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/", "/login").permitAll()
            .anyRequest().authenticated()
        .and()
            .logout()
                .logoutSuccessUrl(logoutUrl)
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .addLogoutHandler(logoutHandler)
        .and()      
            .csrf()
                .csrfTokenRepository(csrfTokenRepository())
        .and()
            .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
        // @formatter:on
    }

@Component
public class CustomLogoutHandler implements LogoutHandler {

    private static Logger logger = Logger.getLogger(CustomLogoutHandler.class);

    @Value("${auth-server}/invalidateTokens")
    private String logoutUrl;

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {

        logger.debug("Excution CustomLogoutHandler for " + authentication.getName());
        Object details = authentication.getDetails();
        if (details.getClass().isAssignableFrom(OAuth2AuthenticationDetails.class)) {

            String accessToken = ((OAuth2AuthenticationDetails) details).getTokenValue();
            RestTemplate restTemplate = new RestTemplate();

            MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
            params.add("access_token", accessToken);

            HttpHeaders headers = new HttpHeaders();
            headers.add("Authorization", "bearer " + accessToken);

            HttpEntity<Object> entity = new HttpEntity<>(params, headers);

            HttpMessageConverter<?> formHttpMessageConverter = new FormHttpMessageConverter();
            HttpMessageConverter<?> stringHttpMessageConverternew = new StringHttpMessageConverter();
            restTemplate.setMessageConverters(Arrays.asList(new HttpMessageConverter[] { formHttpMessageConverter, stringHttpMessageConverternew }));
            try {
                ResponseEntity<String> serverResponse = restTemplate.exchange(logoutUrl, HttpMethod.POST, entity, String.class);
                logger.debug("Server Response : ==> " + serverResponse);
            } catch (HttpClientErrorException e) {
                logger.error("HttpClientErrorException invalidating token with SSO authorization server. response.status code:  " + e.getStatusCode() + ", server URL: " + logoutUrl);
            }
        }
        authentication.setAuthenticated(false);
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        new SecurityContextLogoutHandler().logout(request, response, auth);

    }

}

JDBC tokenStore, .

@Controller
public class AuthenticationController {

    private static Logger logger = Logger.getLogger(AuthenticationController.class);

    @Resource(name = "tokenStore")
    private TokenStore tokenStore;

    @Resource(name = "approvalStore")
    private ApprovalStore approvalStore;

    @RequestMapping(value = "/invalidateTokens", method = RequestMethod.POST)
    public @ResponseBody Map<String, String> revokeAccessToken(HttpServletRequest request, HttpServletResponse response, @RequestParam(name = "access_token") String accessToken, Authentication authentication) {
        if (authentication instanceof OAuth2Authentication) {
            logger.info("Revoking Approvals ==> " + accessToken);
            OAuth2Authentication auth = (OAuth2Authentication) authentication;
            String clientId = auth.getOAuth2Request().getClientId();
            Authentication user = auth.getUserAuthentication();
            if (user != null) {
                Collection<Approval> approvals = new ArrayList<Approval>();
                for (String scope : auth.getOAuth2Request().getScope()) {
                    approvals.add(new Approval(user.getName(), clientId, scope, new Date(), ApprovalStatus.APPROVED));
                }
                approvalStore.revokeApprovals(approvals);
            }
        }
        logger.info("Invalidating access token :- " + accessToken);
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
        if (oAuth2AccessToken != null) {
            if (tokenStore instanceof JdbcTokenStore) {
                logger.info("Invalidating Refresh Token :- " + oAuth2AccessToken.getRefreshToken().getValue());
                ((JdbcTokenStore) tokenStore).removeRefreshToken(oAuth2AccessToken.getRefreshToken());
                tokenStore.removeAccessToken(oAuth2AccessToken);
            }
        }
        Map<String, String> ret = new HashMap<>();
        ret.put("removed_access_token", accessToken);
        return ret;
    }

    @GetMapping("/ssoLogout")
    public void exit(HttpServletRequest request, HttpServletResponse response) throws IOException {
        new SecurityContextLogoutHandler().logout(request, null, null);
        // my authorization server login form can save with remember-me cookie 
        Cookie cookie = new Cookie("my_rememberme_cookie", null);
        cookie.setMaxAge(0);
        cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
        response.addCookie(cookie);
        response.sendRedirect(request.getHeader("referer"));
    }

}

SecurityConfig URL

http
    .requestMatchers()
        .antMatchers(
        "/login"
        ,"/ssoLogout"
        ,"/oauth/authorize"
        ,"/oauth/confirm_access");

, .

+2

JWT, . , .

JWT , api- .

+2

I realized that redirecting to the controller when you exit your client application, and then programmatically exit to your authserver does the trick. This is my configuration in the client application:

@Configuration
@EnableOAuth2Sso
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${auth-server}/exit")
    private String logoutUrl;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .logout()
            .logoutSuccessUrl(logoutUrl)
            .and().authorizeRequests().anyRequest().authenticated();
    }
}

and this is my configuration on my authserver server (this is just a controller that processes the / exit endpoint):

@Controller
public class LogoutController {
    public LogoutController() {
    }

    @RequestMapping({"/exit"})
    public void exit(HttpServletRequest request, HttpServletResponse response) {
        (new SecurityContextLogoutHandler()).logout(request, null, null);

        try {
            response.sendRedirect(request.getHeader("referer"));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

Here is an example application showing a full implementation using JWT. Check it out and let us know if that helps you.

+1
source

Source: https://habr.com/ru/post/1667752/


All Articles