Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
f88ebc06
Commit
f88ebc06
authored
Jul 17, 2018
by
Madhura Bhave
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for OIDC Configuration Provider
Closes gh-13210
parent
4fee54cf
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
187 additions
and
12 deletions
+187
-12
pom.xml
spring-boot-project/spring-boot-autoconfigure/pom.xml
+5
-0
OAuth2ClientProperties.java
...figure/security/oauth2/client/OAuth2ClientProperties.java
+17
-0
OAuth2ClientPropertiesRegistrationAdapter.java
...th2/client/OAuth2ClientPropertiesRegistrationAdapter.java
+30
-0
OAuth2ClientPropertiesRegistrationAdapterTests.java
...lient/OAuth2ClientPropertiesRegistrationAdapterTests.java
+107
-0
pom.xml
spring-boot-project/spring-boot-dependencies/pom.xml
+1
-1
application.yml
...t-sample-oauth2-client/src/main/resources/application.yml
+7
-1
SampleOAuth2ClientApplicationTests.java
...ple/oauth2/client/SampleOAuth2ClientApplicationTests.java
+5
-2
application.yml
...reactive-oauth2-client/src/main/resources/application.yml
+7
-1
SampleReactiveOAuth2ClientApplicationTests.java
...h2/client/SampleReactiveOAuth2ClientApplicationTests.java
+8
-7
No files found.
spring-boot-project/spring-boot-autoconfigure/pom.xml
View file @
f88ebc06
...
...
@@ -725,6 +725,11 @@
<artifactId>
json-path
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
com.squareup.okhttp3
</groupId>
<artifactId>
mockwebserver
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
com.sun.xml.messaging.saaj
</groupId>
<artifactId>
saaj-impl
</artifactId>
...
...
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java
View file @
f88ebc06
...
...
@@ -208,6 +208,15 @@ public class OAuth2ClientProperties {
*/
private
String
jwkSetUri
;
/**
* URI that an OpenID Connect Provider asserts as its Issuer Identifier. If the
* issuer provided is "https://example.com", then an "OpenID Provider
* Configuration Request" will be made to
* "https://example.com/.well-known/openid-configuration". The result is expected
* to be an "OpenID Provider Configuration Response".
*/
private
String
issuerUri
;
public
String
getAuthorizationUri
()
{
return
this
.
authorizationUri
;
}
...
...
@@ -248,6 +257,14 @@ public class OAuth2ClientProperties {
this
.
jwkSetUri
=
jwkSetUri
;
}
public
String
getIssuerUri
()
{
return
this
.
issuerUri
;
}
public
void
setIssuerUri
(
String
issuerUri
)
{
this
.
issuerUri
=
issuerUri
;
}
}
}
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapter.java
View file @
f88ebc06
...
...
@@ -25,6 +25,7 @@ import org.springframework.boot.context.properties.PropertyMapper;
import
org.springframework.boot.convert.ApplicationConversionService
;
import
org.springframework.core.convert.ConversionException
;
import
org.springframework.security.config.oauth2.client.CommonOAuth2Provider
;
import
org.springframework.security.config.oauth2.client.oidc.OidcConfigurationProvider
;
import
org.springframework.security.oauth2.client.registration.ClientRegistration
;
import
org.springframework.security.oauth2.client.registration.ClientRegistration.Builder
;
import
org.springframework.security.oauth2.core.AuthorizationGrantType
;
...
...
@@ -37,6 +38,7 @@ import org.springframework.util.StringUtils;
*
* @author Phillip Webb
* @author Thiago Hirata
* @author Madhura Bhave
* @since 2.1.0
*/
public
final
class
OAuth2ClientPropertiesRegistrationAdapter
{
...
...
@@ -54,6 +56,13 @@ public final class OAuth2ClientPropertiesRegistrationAdapter {
private
static
ClientRegistration
getClientRegistration
(
String
registrationId
,
Registration
properties
,
Map
<
String
,
Provider
>
providers
)
{
String
issuer
=
getIssuerIfPossible
(
registrationId
,
properties
.
getProvider
(),
providers
);
if
(
issuer
!=
null
)
{
return
OidcConfigurationProvider
.
issuer
(
issuer
).
registrationId
(
registrationId
)
.
clientId
(
properties
.
getClientId
())
.
clientSecret
(
properties
.
getClientSecret
()).
build
();
}
Builder
builder
=
getBuilder
(
registrationId
,
properties
.
getProvider
(),
providers
);
PropertyMapper
map
=
PropertyMapper
.
get
().
alwaysApplyingWhenNonNull
();
map
.
from
(
properties:
:
getClientId
).
to
(
builder:
:
clientId
);
...
...
@@ -70,6 +79,27 @@ public final class OAuth2ClientPropertiesRegistrationAdapter {
return
builder
.
build
();
}
private
static
String
getIssuerIfPossible
(
String
registrationId
,
String
configuredProviderId
,
Map
<
String
,
Provider
>
providers
)
{
String
providerId
=
(
configuredProviderId
!=
null
?
configuredProviderId
:
registrationId
);
if
(
providers
.
containsKey
(
providerId
))
{
Provider
provider
=
providers
.
get
(
providerId
);
String
issuer
=
provider
.
getIssuerUri
();
if
(
issuer
!=
null
)
{
return
cleanIssuerPath
(
issuer
);
}
}
return
null
;
}
private
static
String
cleanIssuerPath
(
String
issuer
)
{
if
(
issuer
.
endsWith
(
"/"
))
{
return
issuer
.
substring
(
0
,
issuer
.
length
()
-
1
);
}
return
issuer
;
}
private
static
Builder
getBuilder
(
String
registrationId
,
String
configuredProviderId
,
Map
<
String
,
Provider
>
providers
)
{
String
providerId
=
(
configuredProviderId
!=
null
?
configuredProviderId
...
...
spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java
View file @
f88ebc06
...
...
@@ -17,16 +17,26 @@
package
org
.
springframework
.
boot
.
autoconfigure
.
security
.
oauth2
.
client
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.Map
;
import
okhttp3.mockwebserver.MockResponse
;
import
okhttp3.mockwebserver.MockWebServer
;
import
org.junit.After
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.rules.ExpectedException
;
import
org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
;
import
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Provider
;
import
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties.Registration
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.MediaType
;
import
org.springframework.security.oauth2.client.registration.ClientRegistration
;
import
org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails
;
import
org.springframework.security.oauth2.core.AuthorizationGrantType
;
import
org.springframework.security.oauth2.core.ClientAuthenticationMethod
;
import
org.springframework.security.oauth2.core.oidc.IdTokenClaimNames
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
...
...
@@ -40,6 +50,15 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public
class
OAuth2ClientPropertiesRegistrationAdapterTests
{
private
MockWebServer
server
;
@After
public
void
cleanup
()
throws
Exception
{
if
(
this
.
server
!=
null
)
{
this
.
server
.
shutdown
();
}
}
@Rule
public
ExpectedException
thrown
=
ExpectedException
.
none
();
...
...
@@ -217,4 +236,92 @@ public class OAuth2ClientPropertiesRegistrationAdapterTests {
OAuth2ClientPropertiesRegistrationAdapter
.
getClientRegistrations
(
properties
);
}
@Test
public
void
oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration
()
throws
Exception
{
Registration
registration
=
new
Registration
();
registration
.
setClientId
(
"clientId"
);
registration
.
setClientSecret
(
"clientSecret"
);
testOidcConfiguration
(
registration
,
"okta"
);
}
@Test
public
void
oidcProviderConfigurationWhenProviderSpecifiedOnRegistration
()
throws
Exception
{
Registration
registration
=
new
Registration
();
registration
.
setProvider
(
"okta-oidc"
);
registration
.
setClientId
(
"clientId"
);
registration
.
setClientSecret
(
"clientSecret"
);
testOidcConfiguration
(
registration
,
"okta-oidc"
);
}
private
void
testOidcConfiguration
(
Registration
registration
,
String
providerId
)
throws
Exception
{
this
.
server
=
new
MockWebServer
();
this
.
server
.
start
();
String
issuer
=
this
.
server
.
url
(
""
).
toString
();
String
cleanIssuerPath
=
cleanIssuerPath
(
issuer
);
setupMockResponse
(
cleanIssuerPath
);
OAuth2ClientProperties
properties
=
new
OAuth2ClientProperties
();
Provider
provider
=
new
Provider
();
provider
.
setIssuerUri
(
issuer
);
properties
.
getProvider
().
put
(
providerId
,
provider
);
properties
.
getRegistration
().
put
(
"okta"
,
registration
);
Map
<
String
,
ClientRegistration
>
registrations
=
OAuth2ClientPropertiesRegistrationAdapter
.
getClientRegistrations
(
properties
);
ClientRegistration
adapted
=
registrations
.
get
(
"okta"
);
ProviderDetails
providerDetails
=
adapted
.
getProviderDetails
();
assertThat
(
adapted
.
getClientAuthenticationMethod
())
.
isEqualTo
(
ClientAuthenticationMethod
.
BASIC
);
assertThat
(
adapted
.
getAuthorizationGrantType
())
.
isEqualTo
(
AuthorizationGrantType
.
AUTHORIZATION_CODE
);
assertThat
(
adapted
.
getRegistrationId
()).
isEqualTo
(
"okta"
);
assertThat
(
adapted
.
getClientName
()).
isEqualTo
(
cleanIssuerPath
);
assertThat
(
adapted
.
getScopes
()).
containsOnly
(
"openid"
);
assertThat
(
providerDetails
.
getAuthorizationUri
())
.
isEqualTo
(
"https://example.com/o/oauth2/v2/auth"
);
assertThat
(
providerDetails
.
getTokenUri
())
.
isEqualTo
(
"https://example.com/oauth2/v4/token"
);
assertThat
(
providerDetails
.
getJwkSetUri
())
.
isEqualTo
(
"https://example.com/oauth2/v3/certs"
);
assertThat
(
providerDetails
.
getUserInfoEndpoint
().
getUri
())
.
isEqualTo
(
"https://example.com/oauth2/v3/userinfo"
);
}
private
String
cleanIssuerPath
(
String
issuer
)
{
if
(
issuer
.
endsWith
(
"/"
))
{
return
issuer
.
substring
(
0
,
issuer
.
length
()
-
1
);
}
return
issuer
;
}
private
void
setupMockResponse
(
String
issuer
)
throws
Exception
{
MockResponse
mockResponse
=
new
MockResponse
()
.
setResponseCode
(
HttpStatus
.
OK
.
value
())
.
setBody
(
new
ObjectMapper
().
writeValueAsString
(
getResponse
(
issuer
)))
.
setHeader
(
HttpHeaders
.
CONTENT_TYPE
,
MediaType
.
APPLICATION_JSON_VALUE
);
this
.
server
.
enqueue
(
mockResponse
);
}
private
Map
<
String
,
Object
>
getResponse
(
String
issuer
)
{
Map
<
String
,
Object
>
response
=
new
HashMap
<>();
response
.
put
(
"authorization_endpoint"
,
"https://example.com/o/oauth2/v2/auth"
);
response
.
put
(
"claims_supported"
,
Collections
.
emptyList
());
response
.
put
(
"code_challenge_methods_supported"
,
Collections
.
emptyList
());
response
.
put
(
"id_token_signing_alg_values_supported"
,
Collections
.
emptyList
());
response
.
put
(
"issuer"
,
issuer
);
response
.
put
(
"jwks_uri"
,
"https://example.com/oauth2/v3/certs"
);
response
.
put
(
"response_types_supported"
,
Collections
.
emptyList
());
response
.
put
(
"revocation_endpoint"
,
"https://example.com/o/oauth2/revoke"
);
response
.
put
(
"scopes_supported"
,
Collections
.
singletonList
(
"openid"
));
response
.
put
(
"subject_types_supported"
,
Collections
.
singletonList
(
"public"
));
response
.
put
(
"grant_types_supported"
,
Collections
.
singletonList
(
"authorization_code"
));
response
.
put
(
"token_endpoint"
,
"https://example.com/oauth2/v4/token"
);
response
.
put
(
"token_endpoint_auth_methods_supported"
,
Collections
.
singletonList
(
"client_secret_basic"
));
response
.
put
(
"userinfo_endpoint"
,
"https://example.com/oauth2/v3/userinfo"
);
return
response
;
}
}
spring-boot-project/spring-boot-dependencies/pom.xml
View file @
f88ebc06
...
...
@@ -163,7 +163,7 @@
<spring-plugin.version>
1.2.0.RELEASE
</spring-plugin.version>
<spring-restdocs.version>
2.0.2.BUILD-SNAPSHOT
</spring-restdocs.version>
<spring-retry.version>
1.2.2.RELEASE
</spring-retry.version>
<spring-security.version>
5.1.0.
M1
</spring-security.version>
<spring-security.version>
5.1.0.
BUILD-SNAPSHOT
</spring-security.version>
<spring-session-bom.version>
Apple-SR3
</spring-session-bom.version>
<spring-ws.version>
3.0.1.RELEASE
</spring-ws.version>
<sqlite-jdbc.version>
3.23.1
</sqlite-jdbc.version>
...
...
spring-boot-samples/spring-boot-sample-oauth2-client/src/main/resources/application.yml
View file @
f88ebc06
...
...
@@ -16,4 +16,10 @@ spring:
client-name
:
Github email
provider
:
github
scope
:
user:email
redirect-uri-template
:
http://localhost:8080/login/oauth2/code/github
\ No newline at end of file
redirect-uri-template
:
http://localhost:8080/login/oauth2/code/github
google-oidc
:
client-id
:
${GOOGLE-CLIENT-ID}
client-secret
:
${GOOGLE-CLIENT-SECRET}
provider
:
google-oidc
:
issuer-uri
:
https://accounts.google.com
\ No newline at end of file
spring-boot-samples/spring-boot-sample-oauth2-client/src/test/java/sample/oauth2/client/SampleOAuth2ClientApplicationTests.java
View file @
f88ebc06
...
...
@@ -33,7 +33,9 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith
(
SpringRunner
.
class
)
@SpringBootTest
(
webEnvironment
=
SpringBootTest
.
WebEnvironment
.
RANDOM_PORT
,
properties
=
{
"APP-CLIENT-ID=my-client-id"
,
"APP-CLIENT-SECRET=my-client-secret"
})
"APP-CLIENT-ID=my-client-id"
,
"APP-CLIENT-SECRET=my-client-secret"
,
"GOOGLE-CLIENT-ID=my-google-client-id"
,
"GOOGLE-CLIENT-SECRET=my-google-client-secret"
})
public
class
SampleOAuth2ClientApplicationTests
{
@LocalServerPort
...
...
@@ -55,7 +57,8 @@ public class SampleOAuth2ClientApplicationTests {
ResponseEntity
<
String
>
entity
=
this
.
restTemplate
.
getForEntity
(
"/login"
,
String
.
class
);
assertThat
(
entity
.
getStatusCode
()).
isEqualTo
(
HttpStatus
.
OK
);
assertThat
(
entity
.
getBody
()).
contains
(
"/oauth2/authorization/github-client-1"
);
assertThat
(
entity
.
getBody
()).
contains
(
"/oauth2/authorization/google"
);
assertThat
(
entity
.
getBody
()).
contains
(
"/oauth2/authorization/github-client-2"
);
assertThat
(
entity
.
getBody
()).
contains
(
"/oauth2/authorization/github-client-2"
);
}
...
...
spring-boot-samples/spring-boot-sample-reactive-oauth2-client/src/main/resources/application.yml
View file @
f88ebc06
...
...
@@ -16,4 +16,10 @@ spring:
client-name
:
Github email
provider
:
github
scope
:
user:email
redirect-uri-template
:
http://localhost:8080/login/oauth2/code/github
\ No newline at end of file
redirect-uri-template
:
http://localhost:8080/login/oauth2/code/github
google-oidc
:
client-id
:
${GOOGLE-CLIENT-ID}
client-secret
:
${GOOGLE-CLIENT-SECRET}
provider
:
google-oidc
:
issuer-uri
:
https://accounts.google.com
\ No newline at end of file
spring-boot-samples/spring-boot-sample-reactive-oauth2-client/src/test/java/sample/oauth2/client/SampleReactiveOAuth2ClientApplicationTests.java
View file @
f88ebc06
...
...
@@ -28,7 +28,9 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith
(
SpringRunner
.
class
)
@SpringBootTest
(
webEnvironment
=
SpringBootTest
.
WebEnvironment
.
RANDOM_PORT
,
properties
=
{
"APP-CLIENT-ID=my-client-id"
,
"APP-CLIENT-SECRET=my-client-secret"
})
"APP-CLIENT-ID=my-client-id"
,
"APP-CLIENT-SECRET=my-client-secret"
,
"GOOGLE-CLIENT-ID=my-google-client-id"
,
"GOOGLE-CLIENT-SECRET=my-google-client-secret"
})
public
class
SampleReactiveOAuth2ClientApplicationTests
{
@Autowired
...
...
@@ -36,17 +38,16 @@ public class SampleReactiveOAuth2ClientApplicationTests {
@Test
public
void
everythingShouldRedirectToLogin
()
{
this
.
webTestClient
.
get
().
uri
(
"/"
).
exchange
()
.
expectStatus
().
isFound
()
.
expectHeader
().
valueEquals
(
"Location"
,
"/login"
);
this
.
webTestClient
.
get
().
uri
(
"/"
).
exchange
().
expectStatus
().
isFound
()
.
expectHeader
().
valueEquals
(
"Location"
,
"/login"
);
}
@Test
public
void
loginShouldHaveBothOAuthClientsToChooseFrom
()
{
byte
[]
body
=
this
.
webTestClient
.
get
().
uri
(
"/login"
).
exchange
()
.
expectStatus
().
isOk
()
.
returnResult
(
String
.
class
).
getResponseBodyContent
();
byte
[]
body
=
this
.
webTestClient
.
get
().
uri
(
"/login"
).
exchange
().
expectStatus
()
.
isOk
().
returnResult
(
String
.
class
).
getResponseBodyContent
();
String
bodyString
=
new
String
(
body
);
assertThat
(
bodyString
).
contains
(
"/oauth2/authorization/google"
);
assertThat
(
bodyString
).
contains
(
"/oauth2/authorization/github-client-1"
);
assertThat
(
bodyString
).
contains
(
"/oauth2/authorization/github-client-2"
);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment