Browse Source

Add OAuth2 Kotlin samples to docs

Issue: gh-5558
Eleftheria Stein 5 năm trước cách đây
mục cha
commit
603eb1e647

+ 74 - 3
docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-client.adoc

@@ -19,7 +19,10 @@ In addition, `HttpSecurity.oauth2Client().authorizationCodeGrant()` enables the
 
 The following code shows the complete configuration options provided by the `HttpSecurity.oauth2Client()` DSL:
 
-[source,java]
+.OAuth2 Client Configuration Options
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -41,6 +44,30 @@ public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Client {
+                clientRegistrationRepository = clientRegistrationRepository()
+                authorizedClientRepository = authorizedClientRepository()
+                authorizedClientService = authorizedClientService()
+                authorizationCodeGrant {
+                    authorizationRequestRepository = authorizationRequestRepository()
+                    authorizationRequestResolver = authorizationRequestResolver()
+                    accessTokenResponseClient = accessTokenResponseClient()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 The `OAuth2AuthorizedClientManager` is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more `OAuth2AuthorizedClientProvider`(s).
 
 The following code shows an example of how to register an `OAuth2AuthorizedClientManager` `@Bean` and associate it with an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
@@ -583,7 +610,10 @@ The default implementation of `AuthorizationRequestRepository` is `HttpSessionOA
 
 If you have a custom implementation of `AuthorizationRequestRepository`, you may configure it as shown in the following example:
 
-[source,java]
+.AuthorizationRequestRepository Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -601,6 +631,25 @@ public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Client {
+                authorizationCodeGrant {
+                    authorizationRequestRepository = authorizationRequestRepository()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 
 ===== Requesting an Access Token
 
@@ -645,7 +694,10 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
 
 Whether you customize `DefaultAuthorizationCodeTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
 
-[source,java]
+.Access Token Response Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -663,6 +715,25 @@ public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Client {
+                authorizationCodeGrant {
+                    accessTokenResponseClient = accessTokenResponseClient()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 
 [[oauth2Client-refresh-token-grant]]
 ==== Refresh Token

+ 299 - 9
docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-login.adoc

@@ -283,7 +283,10 @@ public class OAuth2LoginConfig {
 
 The following example shows how to provide a `WebSecurityConfigurerAdapter` with `@EnableWebSecurity` and enable OAuth 2.0 login through `httpSecurity.oauth2Login()`:
 
-[source,java]
+.OAuth2 Login Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -299,13 +302,34 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize(anyRequest, authenticated)
+            }
+            oauth2Login { }
+        }
+    }
+}
+----
+====
+
 
 [[oauth2login-completely-override-autoconfiguration]]
 ==== Completely Override the Auto-configuration
 
 The following example shows how to completely override the auto-configuration by registering a `ClientRegistrationRepository` `@Bean` and providing a `WebSecurityConfigurerAdapter`.
 
-[source,java,attrs="-attributes"]
+.Overriding the auto-configuration
+====
+.Java
+[source,java,role="primary",attrs="-attributes"]
 ----
 @Configuration
 public class OAuth2LoginConfig {
@@ -347,6 +371,50 @@ public class OAuth2LoginConfig {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary",attrs="-attributes"]
+----
+@Configuration
+class OAuth2LoginConfig {
+
+    @EnableWebSecurity
+    class OAuth2LoginSecurityConfig: WebSecurityConfigurerAdapter() {
+
+        override fun configure(http: HttpSecurity) {
+            http {
+                authorizeRequests {
+                    authorize(anyRequest, authenticated)
+                }
+                oauth2Login { }
+            }
+        }
+    }
+
+    @Bean
+    fun clientRegistrationRepository(): ClientRegistrationRepository {
+        return InMemoryClientRegistrationRepository(googleClientRegistration())
+    }
+
+    private fun googleClientRegistration(): ClientRegistration {
+        return ClientRegistration.withRegistrationId("google")
+                .clientId("google-client-id")
+                .clientSecret("google-client-secret")
+                .clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
+                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+                .redirectUriTemplate("{baseUrl}/login/oauth2/code/{registrationId}")
+                .scope("openid", "profile", "email", "address", "phone")
+                .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
+                .tokenUri("https://www.googleapis.com/oauth2/v4/token")
+                .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
+                .userNameAttributeName(IdTokenClaimNames.SUB)
+                .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
+                .clientName("Google")
+                .build()
+    }
+}
+----
+====
+
 
 [[oauth2login-javaconfig-wo-boot]]
 === Java Configuration without Spring Boot 2.x
@@ -407,7 +475,10 @@ For example, `oauth2Login().authorizationEndpoint()` allows configuring the _Aut
 
 The following code shows an example:
 
-[source,java]
+.Advanced OAuth2 Login Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -433,6 +504,34 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login {
+                authorizationEndpoint {
+                    ...
+                }
+                redirectionEndpoint {
+                    ...
+                }
+                tokenEndpoint {
+                    ...
+                }
+                userInfoEndpoint {
+                    ...
+                }
+            }
+        }
+    }
+}
+----
+====
+
 The main goal of the `oauth2Login()` DSL was to closely align with the naming, as defined in the specifications.
 
 The OAuth 2.0 Authorization Framework defines the https://tools.ietf.org/html/rfc6749#section-3[Protocol Endpoints] as follows:
@@ -454,7 +553,10 @@ These claims are normally represented by a JSON object that contains a collectio
 
 The following code shows the complete configuration options available for the `oauth2Login()` DSL:
 
-[source,java]
+.OAuth2 Login Configuration Options
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -489,6 +591,43 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login {
+                clientRegistrationRepository = clientRegistrationRepository()
+                authorizedClientRepository = authorizedClientRepository()
+                authorizedClientService = authorizedClientService()
+                loginPage = "/login"
+                authorizationEndpoint {
+                    baseUri = authorizationRequestBaseUri()
+                    authorizationRequestRepository = authorizationRequestRepository()
+                    authorizationRequestResolver = authorizationRequestResolver()
+                }
+                redirectionEndpoint {
+                    baseUri = authorizationResponseBaseUri()
+                }
+                tokenEndpoint {
+                    accessTokenResponseClient = accessTokenResponseClient()
+                }
+                userInfoEndpoint {
+                    userAuthoritiesMapper = userAuthoritiesMapper()
+                    userService = oauth2UserService()
+                    oidcUserService = oidcUserService()
+                    customUserType(GitHubOAuth2User::class.java, "github")
+                }
+            }
+        }
+    }
+}
+----
+====
+
 The following sections go into more detail on each of the configuration options available:
 
 * <<oauth2login-advanced-login-page, OAuth 2.0 Login Page>>
@@ -521,7 +660,10 @@ To override the default login page, configure `oauth2Login().loginPage()` and (o
 
 The following listing shows an example:
 
-[source,java]
+.OAuth2 Login Page Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -541,6 +683,26 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login {
+                loginPage = "/login/oauth2"
+                authorizationEndpoint {
+                    baseUri = "/login/oauth2/authorization"
+                }
+            }
+        }
+    }
+}
+----
+====
+
 [IMPORTANT]
 You need to provide a `@Controller` with a `@RequestMapping("/login/oauth2")` that is capable of rendering the custom login page.
 
@@ -571,7 +733,10 @@ The default Authorization Response `baseUri` (redirection endpoint) is `*/login/
 
 If you would like to customize the Authorization Response `baseUri`, configure it as shown in the following example:
 
-[source,java]
+.Redirection Endpoint Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -589,6 +754,25 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login {
+                redirectionEndpoint {
+                    baseUri = "/login/oauth2/callback/*"
+                }
+            }
+        }
+    }
+}
+----
+====
+
 [IMPORTANT]
 ====
 You also need to ensure the `ClientRegistration.redirectUriTemplate` matches the custom Authorization Response `baseUri`.
@@ -636,7 +820,10 @@ There are a couple of options to choose from when mapping user authorities:
 
 Provide an implementation of `GrantedAuthoritiesMapper` and configure it as shown in the following example:
 
-[source,java]
+.Granted Authorities Mapper Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -683,9 +870,50 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login {
+                userInfoEndpoint {
+                    userAuthoritiesMapper = userAuthoritiesMapper()
+                }
+            }
+        }
+    }
+
+    private fun userAuthoritiesMapper(): GrantedAuthoritiesMapper = GrantedAuthoritiesMapper { authorities: Collection<GrantedAuthority> ->
+        val mappedAuthorities = emptySet<GrantedAuthority>()
+
+        authorities.forEach { authority ->
+            if (authority is OidcUserAuthority) {
+                val idToken = authority.idToken
+                val userInfo = authority.userInfo
+                // Map the claims found in idToken and/or userInfo
+                // to one or more GrantedAuthority's and add it to mappedAuthorities
+            } else if (authority is OAuth2UserAuthority) {
+                val userAttributes = authority.attributes
+                // Map the attributes found in userAttributes
+                // to one or more GrantedAuthority's and add it to mappedAuthorities
+            }
+        }
+
+        mappedAuthorities
+    }
+}
+----
+====
+
 Alternatively, you may register a `GrantedAuthoritiesMapper` `@Bean` to have it automatically applied to the configuration, as shown in the following example:
 
-[source,java]
+.Granted Authorities Mapper Bean Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -703,6 +931,25 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login { }
+        }
+    }
+
+    @Bean
+    fun userAuthoritiesMapper(): GrantedAuthoritiesMapper {
+        ...
+    }
+}
+----
+====
 
 [[oauth2login-advanced-map-authorities-oauth2userservice]]
 ====== Delegation-based strategy with OAuth2UserService
@@ -713,7 +960,10 @@ The `OAuth2UserRequest` (and `OidcUserRequest`) provides you access to the assoc
 
 The following example shows how to implement and configure a delegation-based strategy using an OpenID Connect 1.0 UserService:
 
-[source,java]
+.OAuth2UserService Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -752,6 +1002,46 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
+
+    override fun configure(http: HttpSecurity) {
+        http {
+            oauth2Login {
+                userInfoEndpoint {
+                    oidcUserService = oidcUserService()
+                }
+            }
+        }
+    }
+
+    @Bean
+    fun oidcUserService(): OAuth2UserService<OidcUserRequest, OidcUser> {
+        val delegate = OidcUserService()
+
+        return OAuth2UserService { userRequest ->
+            // Delegate to the default implementation for loading a user
+            var oidcUser = delegate.loadUser(userRequest)
+
+            val accessToken = userRequest.accessToken
+            val mappedAuthorities = HashSet<GrantedAuthority>()
+
+            // TODO
+            // 1) Fetch the authority information from the protected resource using accessToken
+            // 2) Map the authority information to one or more GrantedAuthority's and add it to mappedAuthorities
+            // 3) Create a copy of oidcUser but use the mappedAuthorities instead
+            oidcUser = DefaultOidcUser(mappedAuthorities, oidcUser.idToken, oidcUser.userInfo)
+
+            oidcUser
+        }
+    }
+}
+----
+====
+
 
 [[oauth2login-advanced-custom-user]]
 ===== Configuring a Custom OAuth2User

+ 249 - 10
docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-resourceserver.adoc

@@ -124,7 +124,10 @@ There are two `@Bean` s that Spring Boot generates on Resource Server's behalf.
 
 The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server. When including `spring-security-oauth2-jose`, this `WebSecurityConfigurerAdapter` looks like:
 
-[source,java]
+.Default JWT Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 protected void configure(HttpSecurity http) {
     http
@@ -135,11 +138,30 @@ protected void configure(HttpSecurity http) {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+fun configure(http: HttpSecurity) {
+    http {
+        authorizeRequests {
+            authorize(anyRequest, authenticated)
+        }
+        oauth2ResourceServer {
+            jwt { }
+        }
+    }
+}
+----
+====
+
 If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
 
 Replacing this is as simple as exposing the bean within the application:
 
-[source,java]
+.Custom JWT Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
@@ -158,6 +180,28 @@ public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize("/messages/**", hasAuthority("SCOPE_message:read"))
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                jwt {
+                    jwtAuthenticationConverter = myConverter()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 The above requires the scope of `message:read` for any URL that starts with `/messages/`.
 
 Methods on the `oauth2ResourceServer` DSL will also override or replace auto configuration.
@@ -184,7 +228,10 @@ And its configuration can be overridden using `jwkSetUri()` or replaced using `d
 
 An authorization server's JWK Set Uri can be configured <<oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or it can be supplied in the DSL:
 
-[source,java]
+.JWK Set Uri Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
@@ -202,6 +249,27 @@ public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                jwt {
+                    jwkSetUri = "https://idp.example.com/.well-known/jwks.json"
+                }
+            }
+        }
+    }
+}
+----
+====
+
 Using `jwkSetUri()` takes precedence over any configuration property.
 
 [[oauth2resourceserver-jwt-decoder-dsl]]
@@ -209,7 +277,10 @@ Using `jwkSetUri()` takes precedence over any configuration property.
 
 More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of `JwtDecoder`:
 
-[source,java]
+.JWT Decoder Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
@@ -227,6 +298,27 @@ public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class DirectlyConfiguredJwtDecoder : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                jwt {
+                    jwtDecoder = myCustomDecoder()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validation,validation>>, <<oauth2resourceserver-jwt-claimsetmapping,mapping>>, or <<oauth2resourceserver-jwt-timeouts,request timeouts>>, is necessary.
 
 [[oauth2resourceserver-jwt-decoder-bean]]
@@ -411,7 +503,10 @@ When this is the case, Resource Server will attempt to coerce these scopes into
 
 This means that to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:
 
-[source,java]
+.Authorization Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
@@ -427,6 +522,27 @@ public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize("/contacts/**", hasAuthority("SCOPE_contacts"))
+                authorize("/messages/**", hasAuthority("SCOPE_messages"))
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                jwt { }
+            }
+        }
+    }
+}
+----
+====
+
 Or similarly with method security:
 
 [source,java]
@@ -444,7 +560,10 @@ Or, at other times, the resource server may need to adapt the attribute or a com
 
 To this end, the DSL exposes `jwtAuthenticationConverter()`:
 
-[source,java]
+.Authorities Extractor Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
@@ -472,6 +591,33 @@ Converter<Jwt, AbstractAuthenticationToken> grantedAuthoritiesExtractor() {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class DirectlyConfiguredJwkSetUri : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                jwt {
+                    jwtAuthenticationConverter = grantedAuthoritiesExtractor()
+                }
+            }
+        }
+    }
+
+    private fun grantedAuthoritiesExtractor(): JwtAuthenticationConverter {
+        val jwtAuthenticationConverter = JwtAuthenticationConverter()
+        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(GrantedAuthoritiesExtractor())
+        return jwtAuthenticationConverter
+    }
+}
+----
+====
+
 which is responsible for converting a `Jwt` into an `Authentication`.
 As part of its configuration, we can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.
 
@@ -812,7 +958,10 @@ There are two `@Bean` s that Spring Boot generates on Resource Server's behalf.
 The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server.
 When use Opaque Token, this `WebSecurityConfigurerAdapter` looks like:
 
-[source,java]
+.Default Opaque Token Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 protected void configure(HttpSecurity http) {
     http
@@ -823,11 +972,30 @@ protected void configure(HttpSecurity http) {
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+override fun configure(http: HttpSecurity) {
+    http {
+        authorizeRequests {
+            authorize(anyRequest, authenticated)
+        }
+        oauth2ResourceServer {
+            opaqueToken { }
+        }
+    }
+}
+----
+====
+
 If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
 
 Replacing this is as simple as exposing the bean within the application:
 
-[source,java]
+.Custom Opaque Token Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
@@ -846,6 +1014,28 @@ public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize("/messages/**", hasAuthority("SCOPE_message:read"))
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                opaqueToken {
+                    introspector = myIntrospector()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 The above requires the scope of `message:read` for any URL that starts with `/messages/`.
 
 Methods on the `oauth2ResourceServer` DSL will also override or replace auto configuration.
@@ -869,7 +1059,10 @@ And its configuration can be overridden using `introspectionUri()` and `introspe
 
 An authorization server's Introspection Uri can be configured <<oauth2resourceserver-opaque-introspectionuri,as a configuration property>> or it can be supplied in the DSL:
 
-[source,java]
+.Introspection URI Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class DirectlyConfiguredIntrospectionUri extends WebSecurityConfigurerAdapter {
@@ -888,6 +1081,28 @@ public class DirectlyConfiguredIntrospectionUri extends WebSecurityConfigurerAda
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class DirectlyConfiguredIntrospectionUri : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                opaqueToken {
+                    introspectionUri = "https://idp.example.com/introspect"
+                    introspectionClientCredentials("client", "secret")
+                }
+            }
+        }
+    }
+}
+----
+====
+
 Using `introspectionUri()` takes precedence over any configuration property.
 
 [[oauth2resourceserver-opaque-introspector-dsl]]
@@ -895,7 +1110,10 @@ Using `introspectionUri()` takes precedence over any configuration property.
 
 More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of `OpaqueTokenIntrospector`:
 
-[source,java]
+.Introspector Configuration
+====
+.Java
+[source,java,role="primary"]
 ----
 @EnableWebSecurity
 public class DirectlyConfiguredIntrospector extends WebSecurityConfigurerAdapter {
@@ -913,6 +1131,27 @@ public class DirectlyConfiguredIntrospector extends WebSecurityConfigurerAdapter
 }
 ----
 
+.Kotlin
+[source,kotlin,role="secondary"]
+----
+@EnableWebSecurity
+class DirectlyConfiguredIntrospector : WebSecurityConfigurerAdapter() {
+    override fun configure(http: HttpSecurity) {
+        http {
+            authorizeRequests {
+                authorize(anyRequest, authenticated)
+            }
+            oauth2ResourceServer {
+                opaqueToken {
+                    introspector = myCustomIntrospector()
+                }
+            }
+        }
+    }
+}
+----
+====
+
 This is handy when deeper configuration, like <<oauth2resourceserver-opaque-authorization-extraction,authority mapping>>, <<oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, or <<oauth2resourceserver-opaque-timeouts,request timeouts>>, is necessary.
 
 [[oauth2resourceserver-opaque-introspector-bean]]