Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot oauth2: No userInfo endpoint - How to load the authentication (Principal) from the JWT access token directly in the client

I am setting up an oauth 2.0 client application which will redirect the users to an external IDP (Authorization Server) to sign in. My app will undergo the regular oauth 2 Authorization code grant flow - 1)Redirect the users to sign in. 2)Obtain the access code first 3) Use the access code to obtain the token. Since the external IDP is using oauth 2 just for authentication, they are not going to provide a user-info endpoint url (required by an OIDC provider) to get the user details. Instead they want us to get the claims from the JWT token directly and make any authorizations in our app. I am unable to find the right code/configuration which will not expect a user-info endpoint and instead decode the jwt directly for loading the authentication. In the below demo code, if I were to decode the user details from the JWT token issued by OKTA without calling its userInfo endpoint, how do I do that?

I am using spring boot 2.x release using the standard oauth client configuration provided in the spring reference sample social oauth2 projects.

I would really appreciate if someone could guide me in the right path. Thank you!

gradle configuration

buildscript {
ext {
    springBootVersion = '2.2.0.RELEASE'
}
repositories {
    mavenLocal()
    mavenCentral()
}
dependencies {
    classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
configurations {
compile.exclude module: 'spring-boot-starter-logging'
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-log4j2:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
compile("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:${springBootVersion}")
compile("org.webjars:jquery:2.1.1")
compile("org.webjars:bootstrap:3.2.0")
compile("org.webjars:webjars-locator-core:0.42")
}

application.yml

github:
  client:
     clientId: <clientId>
     clientSecret: <clientSecret>
     accessTokenUri: https://github.com/login/oauth/access_token
     userAuthorizationUri: https://github.com/login/oauth/authorize
     tokenName: oauth_token
     authenticationScheme: query
     clientAuthenticationScheme: form
  resource:
     userInfoUri: https://api.github.com/user

okta:
  client:
   clientId: <clientId>
   clientSecret: <clientSecret>
   accessTokenUri: https://<okta-sub-domain>/oauth2/default/v1/token
   userAuthorizationUri: https://<okta-sub-domain>/oauth2/default/v1/authorize
   scope: openid profile email
 resource:
  userInfoUri: https://<okta-sub-domain>/oauth2/default/v1/userinfo

OAuth2Config.java

@Configuration
@EnableOAuth2Client
public class Oauth2Config extends WebSecurityConfigurerAdapter {

@Autowired
OAuth2ClientContext oauth2ClientContext;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/**").authorizeRequests()
    .antMatchers("/", "/login**", "/webjars/**", "/error**")
    .permitAll()
  .anyRequest()
    .authenticated()
            .and().logout().logoutSuccessUrl("/").permitAll()
            .and().addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);;
    http.csrf().disable();
}

private Filter ssoFilter() {
    CompositeFilter filter = new CompositeFilter();
    List<Filter> filters = new ArrayList<>();

    OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/github");
    OAuth2RestTemplate githubTemplate = new OAuth2RestTemplate(github(), oauth2ClientContext);
    githubFilter.setRestTemplate(githubTemplate);
    UserInfoTokenServices tokenServices = new UserInfoTokenServices(githubResource().getUserInfoUri(), github().getClientId());
    tokenServices.setRestTemplate(githubTemplate);
    githubFilter.setTokenServices(tokenServices);
    filters.add(githubFilter);

    OAuth2ClientAuthenticationProcessingFilter oktaFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/okta");
    OAuth2RestTemplate oktaTemplate = new OAuth2RestTemplate(okta(), oauth2ClientContext);
    oktaFilter.setRestTemplate(oktaTemplate);
    tokenServices = new UserInfoTokenServices(oktaResource().getUserInfoUri(), okta().getClientId());        
    tokenServices.setRestTemplate(oktaTemplate);
    oktaFilter.setTokenServices(tokenServices);
    filters.add(oktaFilter);

    filter.setFilters(filters);
    return filter;
}

//Client registration
@Bean
@ConfigurationProperties("github.client")
public AuthorizationCodeResourceDetails github() {
    return new AuthorizationCodeResourceDetails();
}

//user info endpoints
@Bean
@ConfigurationProperties("github.resource")
public ResourceServerProperties githubResource() {
    return new ResourceServerProperties();
}

@Bean
@ConfigurationProperties("okta.client")
public AuthorizationCodeResourceDetails okta() {
    return new AuthorizationCodeResourceDetails();
}

@Bean
@ConfigurationProperties("okta.resource")
public ResourceServerProperties oktaResource() {
    return new ResourceServerProperties();
}

//For Handling Redirects
@Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
    FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(filter);
    registration.setOrder(-100);
    return registration;
}

}

A simple controller with an endpoint used by html page

@RestController
public class UserController {

@GetMapping("/user")
public Principal user(Principal principal) {        
    return principal;
}

}

@SpringBootApplication
public class Oauth2Application {

public static void main(String[] args) {
    SpringApplication.run(Oauth2Application.class, args);
}

}

like image 712
atl_dev Avatar asked Nov 06 '19 22:11

atl_dev


People also ask

How do I use JWT for authorization in spring boot?

The use of JWT token for authorization is the most common of its applications. The token is usually generated in the server and sent to the client where it is stored in the session storage or local storage. To access a protected resource the client would send the JWT in the header as given above.

How do I automatically request and refresh OAuth2 client credentials token in spring?

Before making a request to the resource server, first check if the token has already expired or is about to expire. If so, request a new token. Finally, make the request to the resource server. Save the token and expiration time in memory, and have a timer which triggers a token refresh some interval before expiry.


1 Answers

DefaultReactiveOAuth2UserService looks up the userInfo. We can simply introduce a new ReactiveOAuth2UserService implementation to take values from the token, e.g.:

@Service
public class GttOAuth2UserService implements ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User>  {

    @Override
    public Mono<OAuth2User> loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
        final List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("authority"));
        final Map<String, Object> attributes = oAuth2UserRequest.getAdditionalParameters();
        final OAuth2User user = new DefaultOAuth2User(authorities, attributes, "email");
        return Mono.just(user);
    }
}

(in your case it may be the non-reactive equivalents)

like image 197
nagy.zsolt.hun Avatar answered Nov 01 '22 12:11

nagy.zsolt.hun