Okta Spring Boot :: ITs :: OAuth2

Okta OSS parent POM

License

License

Categories

Categories

Spring Boot Container Microservices H2 Data Databases OAuth2 Security
GroupId

GroupId

com.okta.spring
ArtifactId

ArtifactId

okta-spring-boot-integration-tests-oauth2
Last Version

Last Version

2.0.1
Release Date

Release Date

Type

Type

jar
Description

Description

Okta Spring Boot :: ITs :: OAuth2
Okta OSS parent POM
Project Organization

Project Organization

Okta

Download okta-spring-boot-integration-tests-oauth2

How to add to project

<!-- https://jarcasting.com/artifacts/com.okta.spring/okta-spring-boot-integration-tests-oauth2/ -->
<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-boot-integration-tests-oauth2</artifactId>
    <version>2.0.1</version>
</dependency>
// https://jarcasting.com/artifacts/com.okta.spring/okta-spring-boot-integration-tests-oauth2/
implementation 'com.okta.spring:okta-spring-boot-integration-tests-oauth2:2.0.1'
// https://jarcasting.com/artifacts/com.okta.spring/okta-spring-boot-integration-tests-oauth2/
implementation ("com.okta.spring:okta-spring-boot-integration-tests-oauth2:2.0.1")
'com.okta.spring:okta-spring-boot-integration-tests-oauth2:jar:2.0.1'
<dependency org="com.okta.spring" name="okta-spring-boot-integration-tests-oauth2" rev="2.0.1">
  <artifact name="okta-spring-boot-integration-tests-oauth2" type="jar" />
</dependency>
@Grapes(
@Grab(group='com.okta.spring', module='okta-spring-boot-integration-tests-oauth2', version='2.0.1')
)
libraryDependencies += "com.okta.spring" % "okta-spring-boot-integration-tests-oauth2" % "2.0.1"
[com.okta.spring/okta-spring-boot-integration-tests-oauth2 "2.0.1"]

Dependencies

compile (3)

Group / Artifact Type Version
com.okta.spring : okta-spring-boot-starter jar 2.0.1
org.springframework.boot : spring-boot-starter-web jar
org.springframework.boot : spring-boot-starter-security jar

runtime (2)

Group / Artifact Type Version
org.slf4j : jcl-over-slf4j jar 1.7.29
ch.qos.logback : logback-classic jar 1.2.3

test (10)

Group / Artifact Type Version
com.github.tomakehurst : wiremock-standalone jar 2.18.0
io.rest-assured : rest-assured jar 3.0.7
org.hamcrest : hamcrest jar 2.2
com.okta.oidc.tck : okta-oidc-tck jar 0.5.7
org.springframework.boot : spring-boot-test jar
org.springframework : spring-test jar
org.codehaus.groovy : groovy jar 2.5.8
org.codehaus.groovy : groovy-templates jar 2.5.8
org.codehaus.groovy : groovy-json jar 2.5.8
org.testng : testng jar 7.0.0

Project Modules

There are no modules declared in this project.

Maven Central License Support

Okta Spring Boot Starter

Okta's Spring Boot Starter will enable your Spring Boot application to work with Okta via OAuth 2.0/OIDC.

Release status

This library uses semantic versioning and follows Okta's library version policy.

✔️ The current stable major version series is: 2.x

Version Status
0.x.x ⚠️ Retired
1.x.0 🕘 Retiring effective September 28, 2021
2.x.0 ✔️ Stable

Spring Boot Version Compatibility

Okta Spring Boot SDK Versions Compatible Spring Boot Versions
1.2.x 2.1.x
1.4.x 2.2.x
1.5.x 2.4.x
2.0.x 2.4.x

The latest release can always be found on the releases page.

What you need

Quickstart

  1. Create a Spring Boot application with Spring initializr:

    curl https://start.spring.io/starter.tgz -d dependencies=web,okta -d baseDir=<<yourProjectName>> | tar -xzvf -
    cd <<yourProjectName>>
  2. Configure it with Okta CLI:

    okta apps create
  3. Run it:

    ./mvnw spring-boot:run

Include the dependency

For Apache Maven:

<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-boot-starter</artifactId>
</dependency>

For Gradle:

compile 'com.okta.spring:okta-spring-boot-starter'

Supporting client side applications - OAuth Implicit flow

Are you writing backend endpoints in order to support a client side application? If so follow along, otherwise skip to the next section.

Configure your properties

You can configure your applications properties with environment variables, system properties, or configuration files. Take a look at the Spring Boot documentation for more details.

Only these three properties are required for a web app:

Property Default Details
okta.oauth2.issuer N/A Authorization Server issuer URL, i.e.: https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId N/A The Client Id of your Okta OIDC application
okta.oauth2.clientSecret N/A The Client Secret of your Okta OIDC application

There are many more properties that you can optionally configure as well. Here are some examples:

Property Default Details
okta.oauth2.audience api://default The audience of your Authorization Server
okta.oauth2.groupsClaim groups The claim key in the Access Token's JWT that corresponds to an array of the users groups.
okta.oauth2.postLogoutRedirectUri N/A Set to an absolute URI to enable RP-Initiated (SSO) logout.

Create a Controller

The above client makes a request to /hello-oauth, you simply need to create a Spring Boot application and Controller to handle the response:

@SpringBootApplication
@RestController
public class DemoApplication {

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

	@GetMapping("/hello-oauth")
	public String hello(@AuthenticationPrincipal OidcUser user) {
	    return "Hello, " + user.getFullName();
	}
}

That's it!

To test things out you can use curl:

$ curl http://localhost:8080/hello-oauth \
   --header "Authorization: Bearer ${accessToken}"

The result should look something like:

Hello, [email protected]

Okta's Spring Security integration will parse the JWT access token from the HTTP request's Authorization: Bearer header value.

Check out a minimal example that uses the Okta Signin Widget and JQuery or this blog post.

Spring MVC

  1. Setup your MVC project by following Quickstart section above.

  2. Configure the URL mappings for handling GET and POST requests.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {

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

	@GetMapping("/")
	public String index(@AuthenticationPrincipal Jwt jwt) {
		return String.format("Hello, %s!", jwt.getSubject());
	}

	@GetMapping("/message")
	@PreAuthorize("hasAuthority('SCOPE_message:read')")
	public String message() {
		return "secret message";
	}

	@PostMapping("/message")
	@PreAuthorize("hasAuthority('SCOPE_message:write')")
	public String createMessage(@RequestBody String message) {
		return String.format("Message was created. Content: %s", message);
	}
}

NOTE: message:read and message:write used above in @PreAuthorize are OAuth scopes. If you are looking to add custom scopes, refer to the documentation.

  1. Configure your Resource Server either for JWT or Opaque Token validation by extending the WebSecurityConfigurerAdapter class and overriding the configure method. If neither JWT nor Opaque Token is specified in configuration, JWT validation will be used by default.
import com.okta.spring.boot.oauth.Okta;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            // allow anonymous access to the root page
            .antMatchers("/").permitAll()
            // all other requests
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer().jwt(); // replace .jwt() with .opaqueToken() for Opaque Token case

        // Send a 401 message to the browser (w/o this, you'll see a blank page)
        Okta.configureResourceServer401ResponseBody(http);
    }
}

Refer Spring Security documentation here for more details on resource server configuration.

Spring WebFlux

To configure a resource server when using Spring WebFlux, you need to use a couple annotations, and define a SecurityWebFilterChain bean.

import com.okta.spring.boot.oauth.Okta;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@EnableWebFluxSecurity 
@EnableReactiveMethodSecurity 
public class SecurityConfiguration {

    @Bean 
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange()
                .anyExchange().authenticated()
                .and()
            .oauth2ResourceServer()
                .jwt();
                
        // Send a 401 message to the browser (w/o this, you'll see a blank page)
        Okta.configureResourceServer401ResponseBody(http);
                
        return http.build();
    }
}

If you want to support SSO and a resource server in the same application, you can do that too!

@EnableWebFluxSecurity 
@EnableReactiveMethodSecurity 
public class SecurityConfiguration {

    @Bean 
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        http
            .authorizeExchange()
                .anyExchange().authenticated()
                .and()
            .oauth2Login()
                .and()
            .oauth2ResourceServer()
                .jwt();
        return http.build();
    }
}

Full Stack Reactive with Spring WebFlux, WebSockets, and React uses both SSO and a resource server. Its current code uses Spring Security's OIDC support. Changing it to use the Okta Spring Starter reduces the lines of code quite a bit.

Supporting server side applications - OAuth Code flow

Building a server side application and just need to redirect to a login page? This OAuth 2.0 code flow is for you.

Create a Web App on Okta

To create a new OIDC app for Spring Boot on Okta:

  1. Log in to your developer account, navigate to Applications, and click on Add Application.
  2. Select Web and click Next.
  3. Give the application a name and add http://localhost:8080/login/oauth2/code/okta as a login redirect URI.
  4. Click Done.

Configure your properties

You can configure your applications properties with environment variables, system properties, or configuration files. Take a look at the Spring Boot documentation for more details.

Property Required Details
okta.oauth2.issuer true Authorization Server issuer URL, i.e.: https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId true The Client Id of your Okta OIDC application
okta.oauth2.clientSecret true The Client Secret of your Okta OIDC application

Create a simple application

Create a minimal Spring Boot application:

@RestController
@SpringBootApplication
public class ExampleApplication {

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

    @GetMapping("/")
    public String getMessageOfTheDay(@AuthenticationPrincipal OidcUser user) {
        return user.getName() + ", this message of the day is boring";
    }
}

If you want to allow anonymous access to specific routes you can add a WebSecurityConfigurerAdapter:

@Configuration
static class WebConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/my-anon-page").permitAll()
                .anyRequest().authenticated()
            .and().oauth2Client()
            .and().oauth2Login();
    }
}

If you want to add custom claims to JWT tokens in your custom Authorization Server, see Add Custom claim to a token for more info.

You could then extract the attributes from the token by doing something like below:

@RestController
public class ExampleController {

    @GetMapping("/email")
    public String getUserEmail(AbstractOAuth2TokenAuthenticationToken authentication) {
        // AbstractOAuth2TokenAuthenticationToken works for both JWT and opaque access tokens
        return (String) authentication.getTokenAttributes().get("sub");
    }
}

Share Sessions Across Web Servers

The Authorization Code Flow (the typical OAuth redirect) uses sessions. If you have multiple instances of your application, you must configure a Spring Session implementation such as Redis, Hazelcast, JDBC, etc.

That's it!

Open up http://localhost:8080 in your favorite browser.

You'll be redirected automatically to an Okta login page. Once you successfully login, you will be redirected back to your app and you'll see the message of the day!

This module integrates with Spring Security's OAuth support, all you need is the mark your application with the standard @EnableOAuth2Client annotation.

Proxy

If you're running your application (with this okta-spring-boot dependency) from behind a network proxy, you could setup properties for it in application.yml:

okta:
  oauth2:
    proxy:
      host: "proxy.example.com"
      port: 7000
      username: "your-username"             # optional
      password: "your-secret-password"      # optional

or, add JVM args to your application like:

-Dokta.oauth2.proxy.host=proxy.example.com
-Dokta.oauth2.proxy.port=port
-Dokta.oauth2.proxy.username=your-username
-Dokta.oauth2.proxy.password=your-secret-password

or, you could set it programmatically like:

System.setProperty("okta.oauth2.proxy.host", "proxy.example.com");
System.setProperty("okta.oauth2.proxy.port", "7000");
System.setProperty("okta.oauth2.proxy.username", "your-username");
System.setProperty("okta.oauth2.proxy.password", "your-secret-password");

See here for the complete list of properties.

Note: Spring WebFlux (and WebClient) does not support these properties. (See spring-projects/spring-security#8882).

If you are running your Spring Boot App behind a reverse proxy, be sure to read this guide.

Inject the Okta Java SDK

To integrate the Okta Java SDK into your Spring Boot application you just need to add a dependency:

<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-sdk</artifactId>
</dependency>

Then define the okta.client.token property. See creating an API token for more info.

All that is left is to inject the client (com.okta.sdk.client.Client)! Take a look at this post for more info on the best way to inject your beans.

Extra Credit

Want to build this project?

Just clone it and run:

$ git clone https://github.com/okta/okta-spring-boot.git
$ cd okta-spring-boot
$ mvn install
com.okta.spring

Okta, Inc

Versions

Version
2.0.1
2.0.0
1.5.1
1.5.0
1.4.0
1.3.0
1.2.1
1.2.0
1.1.0
1.0.0
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.0
0.2.0