39 Commits
0.0.2 ... 0.0.3

Author SHA1 Message Date
Joe Grandja
db4dd3f08e Release 0.0.3 2020-11-09 14:51:23 -05:00
Joe Grandja
6e2f2fe8a4 Lock Dependency Versions for 0.0.3 release 2020-11-09 14:49:27 -05:00
Joe Grandja
061d9f8c18 Remove spring-security.version from sample 2020-11-09 14:18:36 -05:00
Joe Grandja
59f77d034e Downgrade springSecurityVersion to 5.4.+ 2020-11-09 14:11:00 -05:00
Joe Grandja
bf24cfb19e Add temporary OAuth2RefreshToken2
Issue https://github.com/spring-projects/spring-security/pull/9146
2020-11-09 14:09:34 -05:00
Joe Grandja
77a9b2ebf3 Add temporary OAuth2ErrorCodes2
Issue https://github.com/spring-projects/spring-security/issues/9184
2020-11-09 14:09:34 -05:00
Joe Grandja
d76d209124 Add temporary OAuth2ParameterNames2
Issue https://github.com/spring-projects/spring-security/issues/9183
2020-11-09 14:09:34 -05:00
Joe Grandja
58ad2d2c6c Revert "Set springSecurityVersion to 5.5.0-M1"
This reverts commit 7e2264204b.
2020-11-09 06:52:19 -05:00
Joe Grandja
1a6b3e3e59 Revert "Set springBootVersion to 2.4.0-RC1"
This reverts commit 43b44a1f77.
2020-11-09 05:57:02 -05:00
Joe Grandja
e61639bf7f Set spring-security.version to 5.5.0-M1 in sample 2020-11-09 05:42:27 -05:00
Joe Grandja
7e2264204b Set springSecurityVersion to 5.5.0-M1 2020-11-09 05:30:35 -05:00
Joe Grandja
43b44a1f77 Set springBootVersion to 2.4.0-RC1 2020-11-09 05:11:14 -05:00
Joe Grandja
19d6e97372 Revert "Use reactor-netty-http for snapshot build"
Issue https://github.com/spring-projects/spring-security/issues/8909
2020-11-09 05:10:37 -05:00
Joe Grandja
cff7b786de Set reactorVersion to 2020.0.+ 2020-11-09 05:07:48 -05:00
Joe Grandja
edf23562cb Set springVersion to 5.3.+ 2020-11-09 05:06:45 -05:00
Joe Grandja
e7909d0cdd Update javadoc OAuth2TokenEndpointFilter 2020-11-05 16:57:51 -05:00
Joe Grandja
e49d4a79b4 Polish PublicClientAuthenticationConverter
Commit 5c31fb1b7e
2020-11-05 15:54:24 -05:00
Joe Grandja
7720e275e4 Polish OAuth2ClientAuthenticationProvider
Commit 5c31fb1b7e
2020-11-05 15:23:50 -05:00
Joe Grandja
6a2c841d06 Update OAuth2TokenMetadata.TOKEN_METADATA_BASE
Issue gh-137
2020-11-04 15:39:55 -05:00
Joe Grandja
d7fe79d0ec Update TokenSettings.TOKEN_SETTING_BASE
Issue gh-117
2020-11-04 15:38:48 -05:00
Joe Grandja
40ca7a4654 Update ClientSettings.CLIENT_SETTING_BASE
Issue gh-117
2020-11-04 15:38:05 -05:00
Joe Grandja
06bf391bfa Update javadoc InMemoryOAuth2AuthorizationService
Issue gh-43
2020-11-04 14:49:55 -05:00
Joe Grandja
bfb5432b46 Update javadoc InMemoryRegisteredClientRepository
Issue gh-40
2020-11-04 14:49:55 -05:00
Joe Grandja
9818618ea3 Reuse client authentication assertion
Closes gh-144
2020-11-04 09:28:55 -05:00
Joe Grandja
cb09aef605 Use OAuth2ErrorCodes.UNSUPPORTED_TOKEN_TYPE
Issue gh-83
2020-11-04 07:36:37 -05:00
Joe Grandja
ebcdf7989d Use OAuth2ParameterNames.TOKEN
Issue gh-83
2020-11-03 20:51:46 -05:00
Joe Grandja
df8793c902 Polish tests gh-84 2020-11-02 18:55:43 -05:00
Joe Grandja
6c7486429c Polish gh-88 2020-11-02 18:49:35 -05:00
Joe Grandja
cf82c06502 Polish tests gh-128 2020-11-02 18:43:30 -05:00
Joe Grandja
a2167a5091 Polish gh-128 2020-10-30 11:27:27 -04:00
Alexey Nesterov
78d4bd0bad Add Refresh Token grant type support
Closes gh-50
2020-10-30 11:26:51 -04:00
Alexey Nesterov
1ce77d3caa Bump Spring Security to 5.5 for sample app 2020-10-29 11:16:28 +00:00
Joe Grandja
b7ddb837d6 Polish gh-84 2020-10-28 16:03:17 -04:00
Vivek Babu
dc94e5e161 Implement Token Revocation Endpoint
Closes gh-83
2020-10-24 18:45:46 -04:00
Joe Grandja
18f8b3afaa Enforce one-time use for authorization code
Closes gh-138
2020-10-22 19:43:09 -04:00
Joe Grandja
601640e4fa Set springSecurityVersion to 5.5.+ 2020-10-22 13:41:34 -04:00
Joe Grandja
af60f3d4d0 Introduce OAuth2Tokens
Closes gh-137
2020-10-20 14:43:59 -04:00
Joe Grandja
7b1b965c08 Next Development Version 2020-10-15 05:01:05 -04:00
Joe Grandja
0c6b1251ce Revert "Lock Dependency Versions for 0.0.2 release"
This reverts commit 5471c94615.
2020-10-15 04:59:49 -04:00
100 changed files with 3811 additions and 462 deletions

View File

@@ -4,10 +4,10 @@
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-core:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0

View File

@@ -4,10 +4,10 @@
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-core:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0

View File

@@ -4,10 +4,10 @@
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-core:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0

View File

@@ -4,10 +4,10 @@
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-core:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0

View File

@@ -1,4 +1,4 @@
version=0.0.2
version=0.0.3
springBootVersion=2.4.0-M3
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true

View File

@@ -1,5 +1,5 @@
if (!project.hasProperty("springVersion")) {
ext.springVersion = "5.2.+"
ext.springVersion = "5.3.+"
}
if (!project.hasProperty("springSecurityVersion")) {
@@ -7,7 +7,7 @@ if (!project.hasProperty("springSecurityVersion")) {
}
if (!project.hasProperty("reactorVersion")) {
ext.reactorVersion = "Dysprosium-SR+"
ext.reactorVersion = "2020.0.+"
}
if (!project.hasProperty("locksDisabled")) {
@@ -36,32 +36,3 @@ dependencyManagement {
dependency "com.jayway.jsonpath:json-path:2.+"
}
}
/*
NOTE:
The latest `reactor-netty` dependency was split into `reactor-netty-core` and `reactor-netty-http`,
which resulted in the snapshot build to fail. The below configuration fixes it.
Reference:
- https://github.com/spring-projects/spring-security/issues/8909
- https://github.com/reactor/reactor-netty/issues/739#issuecomment-667047117
*/
if (reactorVersion.startsWith('20')) {
if (reactorVersion.endsWith('SNAPSHOT') || reactorVersion.endsWith('+')) {
ext.reactorLatestVersion = "latest.integration"
} else {
ext.reactorLatestVersion = "latest.release"
}
configurations {
all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
if (details.requested.name == 'reactor-netty') {
details.useTarget("${details.requested.group}:reactor-netty-http:${reactorLatestVersion}")
details.because("reactor-netty is now split into reactor-netty-core and reactor-netty-http")
}
}
}
}
}
}

View File

@@ -6,16 +6,16 @@ com.fasterxml.jackson.core:jackson-core:2.12.0-rc1
com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
org.springframework.security:spring-security-config:5.4.1
org.springframework.security:spring-security-core:5.4.1
org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-web:5.3.0

View File

@@ -6,16 +6,16 @@ com.fasterxml.jackson.core:jackson-core:2.12.0-rc1
com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
org.springframework.security:spring-security-config:5.4.1
org.springframework.security:spring-security-core:5.4.1
org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-web:5.3.0

View File

@@ -6,16 +6,16 @@ com.fasterxml.jackson.core:jackson-core:2.12.0-rc1
com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
org.springframework.security:spring-security-config:5.4.1
org.springframework.security:spring-security-core:5.4.1
org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-web:5.3.0

View File

@@ -6,16 +6,16 @@ com.fasterxml.jackson.core:jackson-core:2.12.0-rc1
com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
org.springframework.security:spring-security-config:5.4.1
org.springframework.security:spring-security-core:5.4.1
org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-web:5.3.0

View File

@@ -6,16 +6,16 @@ com.fasterxml.jackson.core:jackson-core:2.12.0-rc1
com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
org.springframework.security:spring-security-config:5.4.1
org.springframework.security:spring-security-core:5.4.1
org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-web:5.3.0

View File

@@ -7,15 +7,15 @@ com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.jayway.jsonpath:json-path:2.4.0
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
net.minidev:accessors-smart:1.2
net.minidev:json-smart:2.3
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.slf4j:slf4j-api:1.7.25
@@ -25,12 +25,12 @@ org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-test:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-webmvc:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0
org.springframework:spring-web:5.3.0
org.springframework:spring-webmvc:5.3.0

View File

@@ -7,15 +7,15 @@ com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.jayway.jsonpath:json-path:2.4.0
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
net.minidev:accessors-smart:1.2
net.minidev:json-smart:2.3
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.slf4j:slf4j-api:1.7.25
@@ -25,12 +25,12 @@ org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-test:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-webmvc:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0
org.springframework:spring-web:5.3.0
org.springframework:spring-webmvc:5.3.0

View File

@@ -7,15 +7,15 @@ com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.jayway.jsonpath:json-path:2.4.0
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
net.minidev:accessors-smart:1.2
net.minidev:json-smart:2.3
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.slf4j:slf4j-api:1.7.25
@@ -25,12 +25,12 @@ org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-test:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-webmvc:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0
org.springframework:spring-web:5.3.0
org.springframework:spring-webmvc:5.3.0

View File

@@ -7,15 +7,15 @@ com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.jayway.jsonpath:json-path:2.4.0
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
net.minidev:accessors-smart:1.2
net.minidev:json-smart:2.3
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.slf4j:slf4j-api:1.7.25
@@ -25,12 +25,12 @@ org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-test:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-webmvc:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0
org.springframework:spring-web:5.3.0
org.springframework:spring-webmvc:5.3.0

View File

@@ -7,15 +7,15 @@ com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1
com.fasterxml.jackson:jackson-bom:2.12.0-rc1
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.jayway.jsonpath:json-path:2.4.0
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.15
net.bytebuddy:byte-buddy:1.10.15
net.minidev:accessors-smart:1.2
net.minidev:json-smart:2.3
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.hamcrest:hamcrest-core:1.3
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.slf4j:slf4j-api:1.7.25
@@ -25,12 +25,12 @@ org.springframework.security:spring-security-oauth2-core:5.4.1
org.springframework.security:spring-security-oauth2-jose:5.4.1
org.springframework.security:spring-security-test:5.4.1
org.springframework.security:spring-security-web:5.4.1
org.springframework:spring-aop:5.2.9.RELEASE
org.springframework:spring-beans:5.2.9.RELEASE
org.springframework:spring-context:5.2.9.RELEASE
org.springframework:spring-core:5.2.9.RELEASE
org.springframework:spring-expression:5.2.9.RELEASE
org.springframework:spring-jcl:5.2.9.RELEASE
org.springframework:spring-test:5.2.9.RELEASE
org.springframework:spring-web:5.2.9.RELEASE
org.springframework:spring-webmvc:5.2.9.RELEASE
org.springframework:spring-aop:5.3.0
org.springframework:spring-beans:5.3.0
org.springframework:spring-context:5.3.0
org.springframework:spring-core:5.3.0
org.springframework:spring-expression:5.3.0
org.springframework:spring-jcl:5.3.0
org.springframework:spring-test:5.3.0
org.springframework:spring-web:5.3.0
org.springframework:spring-webmvc:5.3.0

View File

@@ -31,11 +31,14 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.web.JwkSetEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
@@ -73,6 +76,8 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
HttpMethod.POST.name()));
private final RequestMatcher tokenEndpointMatcher = new AntPathRequestMatcher(
OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI, HttpMethod.POST.name());
private final RequestMatcher tokenRevocationEndpointMatcher = new AntPathRequestMatcher(
OAuth2TokenRevocationEndpointFilter.DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI, HttpMethod.POST.name());
private final RequestMatcher jwkSetEndpointMatcher = new AntPathRequestMatcher(
JwkSetEndpointFilter.DEFAULT_JWK_SET_ENDPOINT_URI, HttpMethod.GET.name());
@@ -118,8 +123,8 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
* @return a {@code List} of {@link RequestMatcher}'s for the authorization server endpoints
*/
public List<RequestMatcher> getEndpointMatchers() {
return Arrays.asList(this.authorizationEndpointMatcher,
this.tokenEndpointMatcher, this.jwkSetEndpointMatcher);
return Arrays.asList(this.authorizationEndpointMatcher, this.tokenEndpointMatcher,
this.tokenRevocationEndpointMatcher, this.jwkSetEndpointMatcher);
}
@Override
@@ -139,17 +144,29 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
jwtEncoder);
builder.authenticationProvider(postProcess(authorizationCodeAuthenticationProvider));
OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider =
new OAuth2RefreshTokenAuthenticationProvider(
getAuthorizationService(builder),
jwtEncoder);
builder.authenticationProvider(postProcess(refreshTokenAuthenticationProvider));
OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider =
new OAuth2ClientCredentialsAuthenticationProvider(
getAuthorizationService(builder),
jwtEncoder);
builder.authenticationProvider(postProcess(clientCredentialsAuthenticationProvider));
OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider =
new OAuth2TokenRevocationAuthenticationProvider(
getAuthorizationService(builder));
builder.authenticationProvider(postProcess(tokenRevocationAuthenticationProvider));
ExceptionHandlingConfigurer<B> exceptionHandling = builder.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling != null) {
// Register the default AuthenticationEntryPoint for the token endpoint
// Register the default AuthenticationEntryPoint for the token endpoint and token revocation endpoint
exceptionHandling.defaultAuthenticationEntryPointFor(
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), this.tokenEndpointMatcher);
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
new OrRequestMatcher(this.tokenEndpointMatcher, this.tokenRevocationEndpointMatcher));
}
}
@@ -160,8 +177,10 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
OAuth2ClientAuthenticationFilter clientAuthenticationFilter = new OAuth2ClientAuthenticationFilter(
authenticationManager, this.tokenEndpointMatcher);
OAuth2ClientAuthenticationFilter clientAuthenticationFilter =
new OAuth2ClientAuthenticationFilter(
authenticationManager,
new OrRequestMatcher(this.tokenEndpointMatcher, this.tokenRevocationEndpointMatcher));
builder.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class);
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter =
@@ -175,6 +194,11 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
authenticationManager,
getAuthorizationService(builder));
builder.addFilterAfter(postProcess(tokenEndpointFilter), FilterSecurityInterceptor.class);
OAuth2TokenRevocationEndpointFilter tokenRevocationEndpointFilter =
new OAuth2TokenRevocationEndpointFilter(
authenticationManager);
builder.addFilterAfter(postProcess(tokenRevocationEndpointFilter), OAuth2TokenEndpointFilter.class);
}
private static <B extends HttpSecurityBuilder<B>> RegisteredClientRepository getRegisteredClientRepository(B builder) {

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;
/**
* TODO
* This class is temporary and will be removed after upgrading to Spring Security 5.5.0 GA.
*
* @author Joe Grandja
* @since 0.0.3
* @see <a target="_blank" href="https://github.com/spring-projects/spring-security/issues/9184">Issue gh-9184</a>
*/
public interface OAuth2ErrorCodes2 extends OAuth2ErrorCodes {
String UNSUPPORTED_TOKEN_TYPE = "unsupported_token_type";
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;
import java.time.Instant;
/**
* TODO
* This class is temporary and will be removed after upgrading to Spring Security 5.5.0 GA.
*
* @author Joe Grandja
* @since 0.0.3
* @see <a target="_blank" href="https://github.com/spring-projects/spring-security/pull/9146">Issue gh-9146</a>
*/
public class OAuth2RefreshToken2 extends OAuth2RefreshToken {
private final Instant expiresAt;
public OAuth2RefreshToken2(String tokenValue, Instant issuedAt, Instant expiresAt) {
super(tokenValue, issuedAt);
this.expiresAt = expiresAt;
}
@Override
public Instant getExpiresAt() {
return this.expiresAt;
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core.endpoint;
/**
* TODO
* This class is temporary and will be removed after upgrading to Spring Security 5.5.0 GA.
*
* @author Joe Grandja
* @since 0.0.3
* @see <a target="_blank" href="https://github.com/spring-projects/spring-security/issues/9183">Issue gh-9183</a>
*/
public interface OAuth2ParameterNames2 extends OAuth2ParameterNames {
String TOKEN = "token";
String TOKEN_TYPE_HINT = "token_type_hint";
}

View File

@@ -16,7 +16,7 @@
package org.springframework.security.oauth2.server.authorization;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.util.Assert;
import java.io.Serializable;
@@ -27,6 +27,9 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s in-memory.
*
* <p>
* <b>NOTE:</b> This implementation should ONLY be used during development/testing.
*
* @author Krisztian Toth
* @author Joe Grandja
* @since 0.0.1
@@ -64,11 +67,16 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
if (OAuth2AuthorizationAttributeNames.STATE.equals(tokenType.getValue())) {
return token.equals(authorization.getAttribute(OAuth2AuthorizationAttributeNames.STATE));
} else if (TokenType.AUTHORIZATION_CODE.equals(tokenType)) {
return token.equals(authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE));
OAuth2AuthorizationCode authorizationCode = authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
return authorizationCode != null && authorizationCode.getTokenValue().equals(token);
} else if (TokenType.ACCESS_TOKEN.equals(tokenType)) {
return authorization.getAccessToken() != null &&
authorization.getAccessToken().getTokenValue().equals(token);
return authorization.getTokens().getAccessToken() != null &&
authorization.getTokens().getAccessToken().getTokenValue().equals(token);
} else if (TokenType.REFRESH_TOKEN.equals(tokenType)) {
return authorization.getTokens().getRefreshToken() != null &&
authorization.getTokens().getRefreshToken().getTokenValue().equals(token);
}
return false;
}

View File

@@ -15,9 +15,9 @@
*/
package org.springframework.security.oauth2.server.authorization;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import org.springframework.util.Assert;
import java.io.Serializable;
@@ -36,13 +36,17 @@ import java.util.function.Consumer;
* @author Krisztian Toth
* @since 0.0.1
* @see RegisteredClient
* @see OAuth2AccessToken
* @see OAuth2Tokens
*/
public class OAuth2Authorization implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private String registeredClientId;
private String principalName;
private OAuth2Tokens tokens;
@Deprecated
private OAuth2AccessToken accessToken;
private Map<String, Object> attributes;
protected OAuth2Authorization() {
@@ -66,13 +70,23 @@ public class OAuth2Authorization implements Serializable {
return this.principalName;
}
/**
* Returns the {@link OAuth2Tokens}.
*
* @return the {@link OAuth2Tokens}
*/
public OAuth2Tokens getTokens() {
return this.tokens;
}
/**
* Returns the {@link OAuth2AccessToken access token} credential.
*
* @return the {@link OAuth2AccessToken}
*/
@Deprecated
public OAuth2AccessToken getAccessToken() {
return this.accessToken;
return getTokens().getAccessToken();
}
/**
@@ -108,13 +122,13 @@ public class OAuth2Authorization implements Serializable {
OAuth2Authorization that = (OAuth2Authorization) obj;
return Objects.equals(this.registeredClientId, that.registeredClientId) &&
Objects.equals(this.principalName, that.principalName) &&
Objects.equals(this.accessToken, that.accessToken) &&
Objects.equals(this.tokens, that.tokens) &&
Objects.equals(this.attributes, that.attributes);
}
@Override
public int hashCode() {
return Objects.hash(this.registeredClientId, this.principalName, this.accessToken, this.attributes);
return Objects.hash(this.registeredClientId, this.principalName, this.tokens, this.attributes);
}
/**
@@ -138,7 +152,7 @@ public class OAuth2Authorization implements Serializable {
Assert.notNull(authorization, "authorization cannot be null");
return new Builder(authorization.getRegisteredClientId())
.principalName(authorization.getPrincipalName())
.accessToken(authorization.getAccessToken())
.tokens(OAuth2Tokens.from(authorization.getTokens()).build())
.attributes(attrs -> attrs.putAll(authorization.getAttributes()));
}
@@ -149,7 +163,11 @@ public class OAuth2Authorization implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private String registeredClientId;
private String principalName;
private OAuth2Tokens tokens;
@Deprecated
private OAuth2AccessToken accessToken;
private Map<String, Object> attributes = new HashMap<>();
protected Builder(String registeredClientId) {
@@ -167,12 +185,24 @@ public class OAuth2Authorization implements Serializable {
return this;
}
/**
* Sets the {@link OAuth2Tokens}.
*
* @param tokens the {@link OAuth2Tokens}
* @return the {@link Builder}
*/
public Builder tokens(OAuth2Tokens tokens) {
this.tokens = tokens;
return this;
}
/**
* Sets the {@link OAuth2AccessToken access token} credential.
*
* @param accessToken the {@link OAuth2AccessToken}
* @return the {@link Builder}
*/
@Deprecated
public Builder accessToken(OAuth2AccessToken accessToken) {
this.accessToken = accessToken;
return this;
@@ -215,7 +245,14 @@ public class OAuth2Authorization implements Serializable {
OAuth2Authorization authorization = new OAuth2Authorization();
authorization.registeredClientId = this.registeredClientId;
authorization.principalName = this.principalName;
authorization.accessToken = this.accessToken;
if (this.tokens == null) {
OAuth2Tokens.Builder builder = OAuth2Tokens.builder();
if (this.accessToken != null) {
builder.accessToken(this.accessToken);
}
this.tokens = builder.build();
}
authorization.tokens = this.tokens;
authorization.attributes = Collections.unmodifiableMap(this.attributes);
return authorization;
}

View File

@@ -38,6 +38,7 @@ public interface OAuth2AuthorizationAttributeNames {
/**
* The name of the attribute used for the {@link OAuth2ParameterNames#CODE} parameter.
*/
@Deprecated
String CODE = OAuth2Authorization.class.getName().concat(".CODE");
/**

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.security.oauth2.server.authorization;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.util.Assert;
import java.io.Serializable;
@@ -26,6 +25,7 @@ import java.io.Serializable;
public final class TokenType implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
public static final TokenType ACCESS_TOKEN = new TokenType("access_token");
public static final TokenType REFRESH_TOKEN = new TokenType("refresh_token");
public static final TokenType AUTHORIZATION_CODE = new TokenType("authorization_code");
private final String value;

View File

@@ -24,7 +24,7 @@ package org.springframework.security.oauth2.server.authorization;
public final class Version {
private static final int MAJOR = 0;
private static final int MINOR = 0;
private static final int PATCH = 2;
private static final int PATCH = 3;
/**
* Global Serialization value for Spring Security Authorization Server classes.

View File

@@ -15,25 +15,28 @@
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.util.Assert;
import java.util.Collections;
/**
* An {@link Authentication} implementation used when issuing an OAuth 2.0 Access Token.
* An {@link Authentication} implementation used when issuing an
* OAuth 2.0 Access Token and (optional) Refresh Token.
*
* @author Joe Grandja
* @author Madhu Bhat
* @since 0.0.1
* @see AbstractAuthenticationToken
* @see OAuth2AuthorizationCodeAuthenticationProvider
* @see RegisteredClient
* @see OAuth2AccessToken
* @see OAuth2RefreshToken
* @see OAuth2ClientAuthenticationToken
*/
public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {
@@ -41,6 +44,7 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
private final RegisteredClient registeredClient;
private final Authentication clientPrincipal;
private final OAuth2AccessToken accessToken;
private final OAuth2RefreshToken refreshToken;
/**
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters.
@@ -51,6 +55,19 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
*/
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient,
Authentication clientPrincipal, OAuth2AccessToken accessToken) {
this(registeredClient, clientPrincipal, accessToken, null);
}
/**
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters.
*
* @param registeredClient the registered client
* @param clientPrincipal the authenticated client principal
* @param accessToken the access token
* @param refreshToken the refresh token
*/
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken) {
super(Collections.emptyList());
Assert.notNull(registeredClient, "registeredClient cannot be null");
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
@@ -58,6 +75,7 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
this.registeredClient = registeredClient;
this.clientPrincipal = clientPrincipal;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
@Override
@@ -87,4 +105,14 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
public OAuth2AccessToken getAccessToken() {
return this.accessToken;
}
/**
* Returns the {@link OAuth2RefreshToken refresh token}.
*
* @return the {@link OAuth2RefreshToken} or {@code null} if not available
*/
@Nullable
public OAuth2RefreshToken getRefreshToken() {
return this.refreshToken;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenMetadata;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
/**
* Utility methods for the OAuth 2.0 {@link AuthenticationProvider}'s.
*
* @author Joe Grandja
* @since 0.0.3
*/
final class OAuth2AuthenticationProviderUtils {
private OAuth2AuthenticationProviderUtils() {
}
static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {
OAuth2ClientAuthenticationToken clientPrincipal = null;
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
}
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
return clientPrincipal;
}
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
}
static <T extends AbstractOAuth2Token> OAuth2Authorization invalidate(
OAuth2Authorization authorization, T token) {
OAuth2Tokens.Builder builder = OAuth2Tokens.from(authorization.getTokens())
.token(token, OAuth2TokenMetadata.builder().invalidated().build());
if (OAuth2RefreshToken.class.isAssignableFrom(token.getClass())) {
builder.token(
authorization.getTokens().getAccessToken(),
OAuth2TokenMetadata.builder().invalidated().build());
OAuth2AuthorizationCode authorizationCode =
authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
if (authorizationCode != null &&
!authorization.getTokens().getTokenMetadata(authorizationCode).isInvalidated()) {
builder.token(
authorizationCode,
OAuth2TokenMetadata.builder().invalidated().build());
}
}
return OAuth2Authorization.from(authorization)
.tokens(builder.build())
.build();
}
}

View File

@@ -22,12 +22,9 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.JoseHeader;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames;
@@ -35,17 +32,16 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenMetadata;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Set;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
/**
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code Grant.
*
@@ -87,13 +83,8 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
(OAuth2AuthorizationCodeAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal = null;
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authorizationCodeAuthentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) authorizationCodeAuthentication.getPrincipal();
}
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
}
OAuth2ClientAuthenticationToken clientPrincipal =
getAuthenticatedClientElseThrowInvalidClient(authorizationCodeAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
OAuth2Authorization authorization = this.authorizationService.findByToken(
@@ -101,11 +92,18 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica
if (authorization == null) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
OAuth2AuthorizationCode authorizationCode = authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
OAuth2TokenMetadata authorizationCodeMetadata = authorization.getTokens().getTokenMetadata(authorizationCode);
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
if (!registeredClient.getClientId().equals(authorizationRequest.getClientId())) {
if (!authorizationCodeMetadata.isInvalidated()) {
// Invalidate the authorization code given that a different client is attempting to use it
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, authorizationCode);
this.authorizationService.save(authorization);
}
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
@@ -114,40 +112,37 @@ public class OAuth2AuthorizationCodeAuthenticationProvider implements Authentica
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build();
if (authorizationCodeMetadata.isInvalidated()) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
// TODO Allow configuration for issuer claim
URL issuer = null;
try {
issuer = URI.create("https://oauth2.provider.com").toURL();
} catch (MalformedURLException e) { }
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); // TODO Allow configuration for access token time-to-live
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.withClaims()
.issuer(issuer)
.subject(authorization.getPrincipalName())
.audience(Collections.singletonList(registeredClient.getClientId()))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.notBefore(issuedAt)
.claim(OAuth2ParameterNames.SCOPE, authorizedScopes)
.build();
Jwt jwt = this.jwtEncoder.encode(joseHeader, jwtClaimsSet);
Jwt jwt = OAuth2TokenIssuerUtil
.issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), authorizedScopes);
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE));
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), authorizedScopes);
OAuth2Tokens.Builder tokensBuilder = OAuth2Tokens.from(authorization.getTokens())
.accessToken(accessToken);
OAuth2RefreshToken refreshToken = null;
if (registeredClient.getTokenSettings().enableRefreshTokens()) {
refreshToken = OAuth2TokenIssuerUtil.issueRefreshToken(registeredClient.getTokenSettings().refreshTokenTimeToLive());
tokensBuilder.refreshToken(refreshToken);
}
OAuth2Tokens tokens = tokensBuilder.build();
authorization = OAuth2Authorization.from(authorization)
.tokens(tokens)
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
.accessToken(accessToken)
.build();
// Invalidate the authorization code as it can only be used once
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, authorizationCode);
this.authorizationService.save(authorization);
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken);
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken);
}
@Override

View File

@@ -82,15 +82,22 @@ public class OAuth2ClientAuthenticationProvider implements AuthenticationProvide
throwInvalidClient();
}
boolean authenticatedCredentials = false;
if (clientAuthentication.getCredentials() != null) {
String clientSecret = clientAuthentication.getCredentials().toString();
// TODO Use PasswordEncoder.matches()
if (!registeredClient.getClientSecret().equals(clientSecret)) {
throwInvalidClient();
}
authenticatedCredentials = true;
}
authenticatedCredentials = authenticatedCredentials ||
authenticatePkceIfAvailable(clientAuthentication, registeredClient);
if (!authenticatedCredentials) {
throwInvalidClient();
}
return new OAuth2ClientAuthenticationToken(registeredClient);
}
@@ -100,12 +107,12 @@ public class OAuth2ClientAuthenticationProvider implements AuthenticationProvide
return OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication);
}
private void authenticatePkceIfAvailable(OAuth2ClientAuthenticationToken clientAuthentication,
private boolean authenticatePkceIfAvailable(OAuth2ClientAuthenticationToken clientAuthentication,
RegisteredClient registeredClient) {
Map<String, Object> parameters = clientAuthentication.getAdditionalParameters();
if (CollectionUtils.isEmpty(parameters) || !authorizationCodeGrant(parameters)) {
return;
return false;
}
OAuth2Authorization authorization = this.authorizationService.findByToken(
@@ -120,16 +127,19 @@ public class OAuth2ClientAuthenticationProvider implements AuthenticationProvide
String codeChallenge = (String) authorizationRequest.getAdditionalParameters()
.get(PkceParameterNames.CODE_CHALLENGE);
if (StringUtils.hasText(codeChallenge)) {
if (!StringUtils.hasText(codeChallenge) &&
registeredClient.getClientSettings().requireProofKey()) {
throwInvalidClient();
}
String codeChallengeMethod = (String) authorizationRequest.getAdditionalParameters()
.get(PkceParameterNames.CODE_CHALLENGE_METHOD);
String codeVerifier = (String) parameters.get(PkceParameterNames.CODE_VERIFIER);
if (!codeVerifierValid(codeVerifier, codeChallenge, codeChallengeMethod)) {
throwInvalidClient();
}
} else if (registeredClient.getClientSettings().requireProofKey()) {
throwInvalidClient();
}
return true;
}
private static boolean authorizationCodeGrant(Map<String, Object> parameters) {

View File

@@ -18,33 +18,27 @@ package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.JoseHeader;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
/**
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials Grant.
*
@@ -80,15 +74,14 @@ public class OAuth2ClientCredentialsAuthenticationProvider implements Authentica
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
(OAuth2ClientCredentialsAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal = null;
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(clientCredentialsAuthentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) clientCredentialsAuthentication.getPrincipal();
}
if (clientPrincipal == null || !clientPrincipal.isAuthenticated()) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
}
OAuth2ClientAuthenticationToken clientPrincipal =
getAuthenticatedClientElseThrowInvalidClient(clientCredentialsAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.CLIENT_CREDENTIALS)) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT));
}
Set<String> scopes = registeredClient.getScopes(); // Default to configured scopes
if (!CollectionUtils.isEmpty(clientCredentialsAuthentication.getScopes())) {
Set<String> unauthorizedScopes = clientCredentialsAuthentication.getScopes().stream()
@@ -100,36 +93,15 @@ public class OAuth2ClientCredentialsAuthenticationProvider implements Authentica
scopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes());
}
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build();
// TODO Allow configuration for issuer claim
URL issuer = null;
try {
issuer = URI.create("https://oauth2.provider.com").toURL();
} catch (MalformedURLException e) { }
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); // TODO Allow configuration for access token time-to-live
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.withClaims()
.issuer(issuer)
.subject(clientPrincipal.getName())
.audience(Collections.singletonList(registeredClient.getClientId()))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.notBefore(issuedAt)
.claim(OAuth2ParameterNames.SCOPE, scopes)
.build();
Jwt jwt = this.jwtEncoder.encode(joseHeader, jwtClaimsSet);
Jwt jwt = OAuth2TokenIssuerUtil
.issueJwtAccessToken(this.jwtEncoder, clientPrincipal.getName(), registeredClient.getClientId(), scopes);
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes);
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(registeredClient)
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
.principalName(clientPrincipal.getName())
.accessToken(accessToken)
.tokens(OAuth2Tokens.builder().accessToken(accessToken).build())
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
.build();
this.authorizationService.save(authorization);

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import org.springframework.util.Assert;
import java.time.Instant;
import java.util.Set;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
/**
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Refresh Token Grant.
*
* @author Alexey Nesterov
* @since 0.0.3
* @see OAuth2RefreshTokenAuthenticationToken
* @see OAuth2AccessTokenAuthenticationToken
* @see OAuth2AuthorizationService
* @see JwtEncoder
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.5">Section 1.5 Refresh Token Grant</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-6">Section 6 Refreshing an Access Token</a>
*/
public class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider {
private final OAuth2AuthorizationService authorizationService;
private final JwtEncoder jwtEncoder;
/**
* Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided parameters.
*
* @param authorizationService the authorization service
* @param jwtEncoder the jwt encoder
*/
public OAuth2RefreshTokenAuthenticationProvider(OAuth2AuthorizationService authorizationService,
JwtEncoder jwtEncoder) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
Assert.notNull(jwtEncoder, "jwtEncoder cannot be null");
this.authorizationService = authorizationService;
this.jwtEncoder = jwtEncoder;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication =
(OAuth2RefreshTokenAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal =
getAuthenticatedClientElseThrowInvalidClient(refreshTokenAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
OAuth2Authorization authorization = this.authorizationService.findByToken(
refreshTokenAuthentication.getRefreshToken(), TokenType.REFRESH_TOKEN);
if (authorization == null) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
}
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT));
}
Instant refreshTokenExpiresAt = authorization.getTokens().getRefreshToken().getExpiresAt();
if (refreshTokenExpiresAt.isBefore(Instant.now())) {
// As per https://tools.ietf.org/html/rfc6749#section-5.2
// invalid_grant: The provided authorization grant (e.g., authorization code,
// resource owner credentials) or refresh token is invalid, expired, revoked [...].
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
// As per https://tools.ietf.org/html/rfc6749#section-6
// The requested scope MUST NOT include any scope not originally granted by the resource owner,
// and if omitted is treated as equal to the scope originally granted by the resource owner.
Set<String> scopes = refreshTokenAuthentication.getScopes();
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
if (!authorizedScopes.containsAll(scopes)) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE));
}
if (scopes.isEmpty()) {
scopes = authorizedScopes;
}
Jwt jwt = OAuth2TokenIssuerUtil
.issueJwtAccessToken(this.jwtEncoder, authorization.getPrincipalName(), registeredClient.getClientId(), scopes);
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), scopes);
TokenSettings tokenSettings = registeredClient.getTokenSettings();
OAuth2RefreshToken refreshToken;
if (tokenSettings.reuseRefreshTokens()) {
refreshToken = authorization.getTokens().getRefreshToken();
} else {
refreshToken = OAuth2TokenIssuerUtil.issueRefreshToken(tokenSettings.refreshTokenTimeToLive());
}
authorization = OAuth2Authorization.from(authorization)
.tokens(OAuth2Tokens.from(authorization.getTokens()).accessToken(accessToken).refreshToken(refreshToken).build())
.attribute(OAuth2AuthorizationAttributeNames.ACCESS_TOKEN_ATTRIBUTES, jwt)
.build();
this.authorizationService.save(authorization);
return new OAuth2AccessTokenAuthenticationToken(
registeredClient, clientPrincipal, accessToken, refreshToken);
}
@Override
public boolean supports(Class<?> authentication) {
return OAuth2RefreshTokenAuthenticationToken.class.isAssignableFrom(authentication);
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.util.Assert;
import java.util.Collections;
import java.util.Set;
/**
* An {@link Authentication} implementation used for the OAuth 2.0 Refresh Token Grant.
*
* @author Alexey Nesterov
* @since 0.0.3
* @see AbstractAuthenticationToken
* @see OAuth2RefreshTokenAuthenticationProvider
* @see OAuth2ClientAuthenticationToken
*/
public class OAuth2RefreshTokenAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private final String refreshToken;
private final Authentication clientPrincipal;
private final Set<String> scopes;
/**
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
*
* @param refreshToken the refresh token
* @param clientPrincipal the authenticated client principal
*/
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal) {
this(refreshToken, clientPrincipal, Collections.emptySet());
}
/**
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
*
* @param refreshToken the refresh token
* @param clientPrincipal the authenticated client principal
* @param scopes the requested scope(s)
*/
public OAuth2RefreshTokenAuthenticationToken(String refreshToken, Authentication clientPrincipal,
Set<String> scopes) {
super(Collections.emptySet());
Assert.hasText(refreshToken, "refreshToken cannot be empty");
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
Assert.notNull(scopes, "scopes cannot be null");
this.refreshToken = refreshToken;
this.clientPrincipal = clientPrincipal;
this.scopes = scopes;
}
@Override
public Object getPrincipal() {
return this.clientPrincipal;
}
@Override
public Object getCredentials() {
return "";
}
/**
* Returns the refresh token.
*
* @return the refresh token
*/
public String getRefreshToken() {
return this.refreshToken;
}
/**
* Returns the requested scope(s).
*
* @return the requested scope(s), or an empty {@code Set} if not available
*/
public Set<String> getScopes() {
return this.scopes;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken2;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.JoseHeader;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
import java.util.Collections;
import java.util.Set;
/**
* @author Alexey Nesterov
* @since 0.0.3
*/
class OAuth2TokenIssuerUtil {
private static final StringKeyGenerator TOKEN_GENERATOR = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
static Jwt issueJwtAccessToken(JwtEncoder jwtEncoder, String subject, String audience, Set<String> scopes) {
JoseHeader joseHeader = JoseHeader.withAlgorithm(SignatureAlgorithm.RS256).build();
// TODO Allow configuration for issuer claim
URL issuer = null;
try {
issuer = URI.create("https://oauth2.provider.com").toURL();
} catch (MalformedURLException e) { }
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); // TODO Allow configuration for access token time-to-live
JwtClaimsSet jwtClaimsSet = JwtClaimsSet.withClaims()
.issuer(issuer)
.subject(subject)
.audience(Collections.singletonList(audience))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.notBefore(issuedAt)
.claim(OAuth2ParameterNames.SCOPE, scopes)
.build();
return jwtEncoder.encode(joseHeader, jwtClaimsSet);
}
static OAuth2RefreshToken issueRefreshToken(Duration refreshTokenTimeToLive) {
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(refreshTokenTimeToLive);
return new OAuth2RefreshToken2(TOKEN_GENERATOR.generateKey(), issuedAt, expiresAt);
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes2;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
/**
* An {@link AuthenticationProvider} implementation for OAuth 2.0 Token Revocation.
*
* @author Vivek Babu
* @author Joe Grandja
* @since 0.0.3
* @see OAuth2TokenRevocationAuthenticationToken
* @see OAuth2AuthorizationService
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2.1">Section 2.1 Revocation Request</a>
*/
public class OAuth2TokenRevocationAuthenticationProvider implements AuthenticationProvider {
private final OAuth2AuthorizationService authorizationService;
/**
* Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the provided parameters.
*
* @param authorizationService the authorization service
*/
public OAuth2TokenRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
this.authorizationService = authorizationService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication =
(OAuth2TokenRevocationAuthenticationToken) authentication;
OAuth2ClientAuthenticationToken clientPrincipal =
getAuthenticatedClientElseThrowInvalidClient(tokenRevocationAuthentication);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
TokenType tokenType = null;
String tokenTypeHint = tokenRevocationAuthentication.getTokenTypeHint();
if (StringUtils.hasText(tokenTypeHint)) {
if (TokenType.REFRESH_TOKEN.getValue().equals(tokenTypeHint)) {
tokenType = TokenType.REFRESH_TOKEN;
} else if (TokenType.ACCESS_TOKEN.getValue().equals(tokenTypeHint)) {
tokenType = TokenType.ACCESS_TOKEN;
} else {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes2.UNSUPPORTED_TOKEN_TYPE));
}
}
OAuth2Authorization authorization = this.authorizationService.findByToken(
tokenRevocationAuthentication.getToken(), tokenType);
if (authorization == null) {
// Return the authentication request when token not found
return tokenRevocationAuthentication;
}
if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
}
AbstractOAuth2Token token = authorization.getTokens().getToken(tokenRevocationAuthentication.getToken());
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, token);
this.authorizationService.save(authorization);
return new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal);
}
@Override
public boolean supports(Class<?> authentication) {
return OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.util.Assert;
import java.util.Collections;
/**
* An {@link Authentication} implementation used for OAuth 2.0 Token Revocation.
*
* @author Vivek Babu
* @author Joe Grandja
* @since 0.0.3
* @see AbstractAuthenticationToken
* @see OAuth2TokenRevocationAuthenticationProvider
*/
public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private final String token;
private final Authentication clientPrincipal;
private final String tokenTypeHint;
/**
* Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided parameters.
*
* @param token the token
* @param clientPrincipal the authenticated client principal
* @param tokenTypeHint the token type hint
*/
public OAuth2TokenRevocationAuthenticationToken(String token,
Authentication clientPrincipal, @Nullable String tokenTypeHint) {
super(Collections.emptyList());
Assert.hasText(token, "token cannot be empty");
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
this.token = token;
this.clientPrincipal = clientPrincipal;
this.tokenTypeHint = tokenTypeHint;
}
/**
* Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided parameters.
*
* @param revokedToken the revoked token
* @param clientPrincipal the authenticated client principal
*/
public OAuth2TokenRevocationAuthenticationToken(AbstractOAuth2Token revokedToken,
Authentication clientPrincipal) {
super(Collections.emptyList());
Assert.notNull(revokedToken, "revokedToken cannot be null");
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
this.token = revokedToken.getTokenValue();
this.clientPrincipal = clientPrincipal;
this.tokenTypeHint = null;
setAuthenticated(true); // Indicates that the token was authenticated and revoked
}
@Override
public Object getPrincipal() {
return this.clientPrincipal;
}
@Override
public Object getCredentials() {
return "";
}
/**
* Returns the token.
*
* @return the token
*/
public String getToken() {
return this.token;
}
/**
* Returns the token type hint.
*
* @return the token type hint
*/
@Nullable
public String getTokenTypeHint() {
return this.tokenTypeHint;
}
}

View File

@@ -25,6 +25,9 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* A {@link RegisteredClientRepository} that stores {@link RegisteredClient}(s) in-memory.
*
* <p>
* <b>NOTE:</b> This implementation is recommended ONLY to be used during development/testing.
*
* @author Anoop Garlapati
* @see RegisteredClientRepository
* @see RegisteredClient

View File

@@ -26,7 +26,7 @@ import java.util.Map;
* @see Settings
*/
public class ClientSettings extends Settings {
private static final String CLIENT_SETTING_BASE = "spring.security.oauth2.authorization-server.client.";
private static final String CLIENT_SETTING_BASE = "setting.client.";
public static final String REQUIRE_PROOF_KEY = CLIENT_SETTING_BASE.concat("require-proof-key");
public static final String REQUIRE_USER_CONSENT = CLIENT_SETTING_BASE.concat("require-user-consent");

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.security.oauth2.server.authorization.config;
import org.springframework.util.Assert;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@@ -27,8 +29,11 @@ import java.util.Map;
* @see Settings
*/
public class TokenSettings extends Settings {
private static final String TOKEN_SETTING_BASE = "spring.security.oauth2.authorization-server.token.";
private static final String TOKEN_SETTING_BASE = "setting.token.";
public static final String ACCESS_TOKEN_TIME_TO_LIVE = TOKEN_SETTING_BASE.concat("access-token-time-to-live");
public static final String ENABLE_REFRESH_TOKENS = TOKEN_SETTING_BASE.concat("enable-refresh-tokens");
public static final String REUSE_REFRESH_TOKENS = TOKEN_SETTING_BASE.concat("reuse-refresh-tokens");
public static final String REFRESH_TOKEN_TIME_TO_LIVE = TOKEN_SETTING_BASE.concat("refresh-token-time-to-live");
/**
* Constructs a {@code TokenSettings}.
@@ -56,19 +61,86 @@ public class TokenSettings extends Settings {
}
/**
* Set the time-to-live for an access token.
* Set the time-to-live for an access token. Must be greater than {@code Duration.ZERO}.
*
* @param accessTokenTimeToLive the time-to-live for an access token
* @return the {@link TokenSettings}
*/
public TokenSettings accessTokenTimeToLive(Duration accessTokenTimeToLive) {
Assert.notNull(accessTokenTimeToLive, "accessTokenTimeToLive cannot be null");
Assert.isTrue(accessTokenTimeToLive.getSeconds() > 0, "accessTokenTimeToLive must be greater than Duration.ZERO");
setting(ACCESS_TOKEN_TIME_TO_LIVE, accessTokenTimeToLive);
return this;
}
/**
* Returns {@code true} if refresh tokens are enabled. The default is {@code true}.
*
* @return {@code true} if refresh tokens are enabled, {@code false} otherwise
*/
public boolean enableRefreshTokens() {
return setting(ENABLE_REFRESH_TOKENS);
}
/**
* Set to {@code true} to enable refresh tokens.
*
* @param enableRefreshTokens {@code true} to enable refresh tokens, {@code false} otherwise
* @return the {@link TokenSettings}
*/
public TokenSettings enableRefreshTokens(boolean enableRefreshTokens) {
setting(ENABLE_REFRESH_TOKENS, enableRefreshTokens);
return this;
}
/**
* Returns {@code true} if refresh tokens are reused when returning the access token response,
* or {@code false} if a new refresh token is issued. The default is {@code true}.
*/
public boolean reuseRefreshTokens() {
return setting(REUSE_REFRESH_TOKENS);
}
/**
* Set to {@code true} if refresh tokens are reused when returning the access token response,
* or {@code false} if a new refresh token is issued.
*
* @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false} to issue new refresh tokens
* @return the {@link TokenSettings}
*/
public TokenSettings reuseRefreshTokens(boolean reuseRefreshTokens) {
setting(REUSE_REFRESH_TOKENS, reuseRefreshTokens);
return this;
}
/**
* Returns the time-to-live for a refresh token. The default is 60 minutes.
*
* @return the time-to-live for a refresh token
*/
public Duration refreshTokenTimeToLive() {
return setting(REFRESH_TOKEN_TIME_TO_LIVE);
}
/**
* Set the time-to-live for a refresh token. Must be greater than {@code Duration.ZERO}.
*
* @param refreshTokenTimeToLive the time-to-live for a refresh token
* @return the {@link TokenSettings}
*/
public TokenSettings refreshTokenTimeToLive(Duration refreshTokenTimeToLive) {
Assert.notNull(refreshTokenTimeToLive, "refreshTokenTimeToLive cannot be null");
Assert.isTrue(refreshTokenTimeToLive.getSeconds() > 0, "refreshTokenTimeToLive must be greater than Duration.ZERO");
setting(REFRESH_TOKEN_TIME_TO_LIVE, refreshTokenTimeToLive);
return this;
}
protected static Map<String, Object> defaultSettings() {
Map<String, Object> settings = new HashMap<>();
settings.put(ACCESS_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(5));
settings.put(ENABLE_REFRESH_TOKENS, true);
settings.put(REUSE_REFRESH_TOKENS, true);
settings.put(REFRESH_TOKEN_TIME_TO_LIVE, Duration.ofMinutes(60));
return settings;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import java.time.Instant;
/**
* An implementation of an {@link AbstractOAuth2Token}
* representing an OAuth 2.0 Authorization Code Grant.
*
* @author Joe Grandja
* @since 0.0.3
* @see AbstractOAuth2Token
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
*/
public class OAuth2AuthorizationCode extends AbstractOAuth2Token {
/**
* Constructs an {@code OAuth2AuthorizationCode} using the provided parameters.
* @param tokenValue the token value
* @param issuedAt the time at which the token was issued
* @param expiresAt the time at which the token expires
*/
public OAuth2AuthorizationCode(String tokenValue, Instant issuedAt, Instant expiresAt) {
super(tokenValue, issuedAt, expiresAt);
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Holds metadata associated to an OAuth 2.0 Token.
*
* @author Joe Grandja
* @since 0.0.3
* @see OAuth2Tokens
*/
public class OAuth2TokenMetadata implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
protected static final String TOKEN_METADATA_BASE = "metadata.token.";
/**
* The name of the metadata that indicates if the token has been invalidated.
*/
public static final String INVALIDATED = TOKEN_METADATA_BASE.concat("invalidated");
private final Map<String, Object> metadata;
protected OAuth2TokenMetadata(Map<String, Object> metadata) {
this.metadata = Collections.unmodifiableMap(new HashMap<>(metadata));
}
/**
* Returns {@code true} if the token has been invalidated (e.g. revoked).
* The default is {@code false}.
*
* @return {@code true} if the token has been invalidated, {@code false} otherwise
*/
public boolean isInvalidated() {
return getMetadata(INVALIDATED);
}
/**
* Returns the value of the metadata associated to the token.
*
* @param name the name of the metadata
* @param <T> the type of the metadata
* @return the value of the metadata, or {@code null} if not available
*/
@SuppressWarnings("unchecked")
public <T> T getMetadata(String name) {
Assert.hasText(name, "name cannot be empty");
return (T) this.metadata.get(name);
}
/**
* Returns the metadata associated to the token.
*
* @return a {@code Map} of the metadata
*/
public Map<String, Object> getMetadata() {
return this.metadata;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
OAuth2TokenMetadata that = (OAuth2TokenMetadata) obj;
return Objects.equals(this.metadata, that.metadata);
}
@Override
public int hashCode() {
return Objects.hash(this.metadata);
}
/**
* Returns a new {@link Builder}.
*
* @return the {@link Builder}
*/
public static Builder builder() {
return new Builder();
}
/**
* A builder for {@link OAuth2TokenMetadata}.
*/
public static class Builder implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private final Map<String, Object> metadata = defaultMetadata();
protected Builder() {
}
/**
* Set the token as invalidated (e.g. revoked).
*
* @return the {@link Builder}
*/
public Builder invalidated() {
metadata(INVALIDATED, true);
return this;
}
/**
* Adds a metadata associated to the token.
*
* @param name the name of the metadata
* @param value the value of the metadata
* @return the {@link Builder}
*/
public Builder metadata(String name, Object value) {
Assert.hasText(name, "name cannot be empty");
Assert.notNull(value, "value cannot be null");
this.metadata.put(name, value);
return this;
}
/**
* A {@code Consumer} of the metadata {@code Map}
* allowing the ability to add, replace, or remove.
*
* @param metadataConsumer a {@link Consumer} of the metadata {@code Map}
* @return the {@link Builder}
*/
public Builder metadata(Consumer<Map<String, Object>> metadataConsumer) {
metadataConsumer.accept(this.metadata);
return this;
}
/**
* Builds a new {@link OAuth2TokenMetadata}.
*
* @return the {@link OAuth2TokenMetadata}
*/
public OAuth2TokenMetadata build() {
return new OAuth2TokenMetadata(this.metadata);
}
protected static Map<String, Object> defaultMetadata() {
Map<String, Object> metadata = new HashMap<>();
metadata.put(INVALIDATED, false);
return metadata;
}
}
}

View File

@@ -0,0 +1,292 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken2;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.Version;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* A container for OAuth 2.0 Tokens.
*
* @author Joe Grandja
* @since 0.0.3
* @see OAuth2Authorization
* @see OAuth2TokenMetadata
* @see AbstractOAuth2Token
* @see OAuth2AccessToken
* @see OAuth2RefreshToken
*/
public class OAuth2Tokens implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private final Map<Class<? extends AbstractOAuth2Token>, OAuth2TokenHolder> tokens;
protected OAuth2Tokens(Map<Class<? extends AbstractOAuth2Token>, OAuth2TokenHolder> tokens) {
this.tokens = new HashMap<>(tokens);
}
/**
* Returns the {@link OAuth2AccessToken access token}.
*
* @return the {@link OAuth2AccessToken}, or {@code null} if not available
*/
@Nullable
public OAuth2AccessToken getAccessToken() {
return getToken(OAuth2AccessToken.class);
}
/**
* Returns the {@link OAuth2RefreshToken refresh token}.
*
* @return the {@link OAuth2RefreshToken}, or {@code null} if not available
*/
@Nullable
public OAuth2RefreshToken getRefreshToken() {
OAuth2RefreshToken refreshToken = getToken(OAuth2RefreshToken.class);
return refreshToken != null ? refreshToken : getToken(OAuth2RefreshToken2.class);
}
/**
* Returns the token specified by {@code tokenType}.
*
* @param tokenType the token type
* @param <T> the type of the token
* @return the token, or {@code null} if not available
*/
@Nullable
@SuppressWarnings("unchecked")
public <T extends AbstractOAuth2Token> T getToken(Class<T> tokenType) {
Assert.notNull(tokenType, "tokenType cannot be null");
OAuth2TokenHolder tokenHolder = this.tokens.get(tokenType);
return tokenHolder != null ? (T) tokenHolder.getToken() : null;
}
/**
* Returns the token specified by {@code token}.
*
* @param token the token
* @param <T> the type of the token
* @return the token, or {@code null} if not available
*/
@Nullable
@SuppressWarnings("unchecked")
public <T extends AbstractOAuth2Token> T getToken(String token) {
Assert.hasText(token, "token cannot be empty");
OAuth2TokenHolder tokenHolder = this.tokens.values().stream()
.filter(holder -> holder.getToken().getTokenValue().equals(token))
.findFirst()
.orElse(null);
return tokenHolder != null ? (T) tokenHolder.getToken() : null;
}
/**
* Returns the token metadata associated to the provided {@code token}.
*
* @param token the token
* @param <T> the type of the token
* @return the token metadata, or {@code null} if not available
*/
@Nullable
public <T extends AbstractOAuth2Token> OAuth2TokenMetadata getTokenMetadata(T token) {
Assert.notNull(token, "token cannot be null");
OAuth2TokenHolder tokenHolder = this.tokens.get(token.getClass());
return (tokenHolder != null && tokenHolder.getToken().equals(token)) ?
tokenHolder.getTokenMetadata() : null;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
OAuth2Tokens that = (OAuth2Tokens) obj;
return Objects.equals(this.tokens, that.tokens);
}
@Override
public int hashCode() {
return Objects.hash(this.tokens);
}
/**
* Returns a new {@link Builder}.
*
* @return the {@link Builder}
*/
public static Builder builder() {
return new Builder();
}
/**
* Returns a new {@link Builder}, initialized with the values from the provided {@code tokens}.
*
* @param tokens the tokens used for initializing the {@link Builder}
* @return the {@link Builder}
*/
public static Builder from(OAuth2Tokens tokens) {
Assert.notNull(tokens, "tokens cannot be null");
return new Builder(tokens.tokens);
}
/**
* A builder for {@link OAuth2Tokens}.
*/
public static class Builder implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private Map<Class<? extends AbstractOAuth2Token>, OAuth2TokenHolder> tokens;
protected Builder() {
this.tokens = new HashMap<>();
}
protected Builder(Map<Class<? extends AbstractOAuth2Token>, OAuth2TokenHolder> tokens) {
this.tokens = new HashMap<>(tokens);
}
/**
* Sets the {@link OAuth2AccessToken access token}.
*
* @param accessToken the {@link OAuth2AccessToken}
* @return the {@link Builder}
*/
public Builder accessToken(OAuth2AccessToken accessToken) {
return addToken(accessToken, null);
}
/**
* Sets the {@link OAuth2AccessToken access token} and associated {@link OAuth2TokenMetadata token metadata}.
*
* @param accessToken the {@link OAuth2AccessToken}
* @param tokenMetadata the {@link OAuth2TokenMetadata}
* @return the {@link Builder}
*/
public Builder accessToken(OAuth2AccessToken accessToken, OAuth2TokenMetadata tokenMetadata) {
return addToken(accessToken, tokenMetadata);
}
/**
* Sets the {@link OAuth2RefreshToken refresh token}.
*
* @param refreshToken the {@link OAuth2RefreshToken}
* @return the {@link Builder}
*/
public Builder refreshToken(OAuth2RefreshToken refreshToken) {
return addToken(refreshToken, null);
}
/**
* Sets the {@link OAuth2RefreshToken refresh token} and associated {@link OAuth2TokenMetadata token metadata}.
*
* @param refreshToken the {@link OAuth2RefreshToken}
* @param tokenMetadata the {@link OAuth2TokenMetadata}
* @return the {@link Builder}
*/
public Builder refreshToken(OAuth2RefreshToken refreshToken, OAuth2TokenMetadata tokenMetadata) {
return addToken(refreshToken, tokenMetadata);
}
/**
* Sets the token.
*
* @param token the token
* @param <T> the type of the token
* @return the {@link Builder}
*/
public <T extends AbstractOAuth2Token> Builder token(T token) {
return addToken(token, null);
}
/**
* Sets the token and associated {@link OAuth2TokenMetadata token metadata}.
*
* @param token the token
* @param tokenMetadata the {@link OAuth2TokenMetadata}
* @param <T> the type of the token
* @return the {@link Builder}
*/
public <T extends AbstractOAuth2Token> Builder token(T token, OAuth2TokenMetadata tokenMetadata) {
return addToken(token, tokenMetadata);
}
protected Builder addToken(AbstractOAuth2Token token, OAuth2TokenMetadata tokenMetadata) {
Assert.notNull(token, "token cannot be null");
if (tokenMetadata == null) {
tokenMetadata = OAuth2TokenMetadata.builder().build();
}
this.tokens.put(token.getClass(), new OAuth2TokenHolder(token, tokenMetadata));
return this;
}
/**
* Builds a new {@link OAuth2Tokens}.
*
* @return the {@link OAuth2Tokens}
*/
public OAuth2Tokens build() {
return new OAuth2Tokens(this.tokens);
}
}
protected static class OAuth2TokenHolder implements Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
private final AbstractOAuth2Token token;
private final OAuth2TokenMetadata tokenMetadata;
protected OAuth2TokenHolder(AbstractOAuth2Token token, OAuth2TokenMetadata tokenMetadata) {
this.token = token;
this.tokenMetadata = tokenMetadata;
}
protected AbstractOAuth2Token getToken() {
return this.token;
}
protected OAuth2TokenMetadata getTokenMetadata() {
return this.tokenMetadata;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
OAuth2TokenHolder that = (OAuth2TokenHolder) obj;
return Objects.equals(this.token, that.token) &&
Objects.equals(this.tokenMetadata, that.tokenMetadata);
}
@Override
public int hashCode() {
return Objects.hash(this.token, this.tokenMetadata);
}
}
}

View File

@@ -36,6 +36,8 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@@ -53,6 +55,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
@@ -184,9 +188,12 @@ public class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {
UserConsentPage.displayConsent(request, response, registeredClient, authorization);
} else {
String code = this.codeGenerator.generateKey();
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(5, ChronoUnit.MINUTES); // TODO Allow configuration for authorization code time-to-live
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(
this.codeGenerator.generateKey(), issuedAt, expiresAt);
OAuth2Authorization authorization = builder
.attribute(OAuth2AuthorizationAttributeNames.CODE, code)
.tokens(OAuth2Tokens.builder().token(authorizationCode).build())
.attribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES, authorizationRequest.getScopes())
.build();
this.authorizationService.save(authorization);
@@ -200,7 +207,7 @@ public class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {
// The authorization code is bound to the client identifier and redirection URI.
sendAuthorizationResponse(request, response,
authorizationRequestContext.resolveRedirectUri(), code, authorizationRequest.getState());
authorizationRequestContext.resolveRedirectUri(), authorizationCode, authorizationRequest.getState());
}
}
@@ -232,18 +239,21 @@ public class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {
return;
}
String code = this.codeGenerator.generateKey();
Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(5, ChronoUnit.MINUTES); // TODO Allow configuration for authorization code time-to-live
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(
this.codeGenerator.generateKey(), issuedAt, expiresAt);
OAuth2Authorization authorization = OAuth2Authorization.from(userConsentRequestContext.getAuthorization())
.tokens(OAuth2Tokens.builder().token(authorizationCode).build())
.attributes(attrs -> {
attrs.remove(OAuth2AuthorizationAttributeNames.STATE);
attrs.put(OAuth2AuthorizationAttributeNames.CODE, code);
attrs.put(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES, userConsentRequestContext.getScopes());
})
.build();
this.authorizationService.save(authorization);
sendAuthorizationResponse(request, response, userConsentRequestContext.resolveRedirectUri(),
code, userConsentRequestContext.getAuthorizationRequest().getState());
authorizationCode, userConsentRequestContext.getAuthorizationRequest().getState());
}
private void validateAuthorizationRequest(OAuth2AuthorizationRequestContext authorizationRequestContext) {
@@ -389,11 +399,11 @@ public class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter {
}
private void sendAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,
String redirectUri, String code, String state) throws IOException {
String redirectUri, OAuth2AuthorizationCode authorizationCode, String state) throws IOException {
UriComponentsBuilder uriBuilder = UriComponentsBuilder
.fromUriString(redirectUri)
.queryParam(OAuth2ParameterNames.CODE, code);
.queryParam(OAuth2ParameterNames.CODE, authorizationCode.getTokenValue());
if (StringUtils.hasText(state)) {
uriBuilder.queryParam(OAuth2ParameterNames.STATE, state);
}

View File

@@ -28,14 +28,19 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
@@ -57,19 +62,18 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
* A {@code Filter} for the OAuth 2.0 Authorization Code Grant,
* which handles the processing of the OAuth 2.0 Access Token Request.
* A {@code Filter} for the OAuth 2.0 Token endpoint,
* which handles the processing of an OAuth 2.0 Authorization Grant.
*
* <p>
* It converts the OAuth 2.0 Access Token Request to an {@link OAuth2AuthorizationCodeAuthenticationToken},
* It converts the OAuth 2.0 Authorization Grant request to an {@link Authentication},
* which is then authenticated by the {@link AuthenticationManager}.
* If the authentication succeeds, the {@link AuthenticationManager} returns an
* {@link OAuth2AccessTokenAuthenticationToken}, which contains
* the {@link OAuth2AccessToken} that is returned in the response.
* In case of any error, an {@link OAuth2Error} is returned in the response.
* {@link OAuth2AccessTokenAuthenticationToken}, which is returned in the OAuth 2.0 Access Token response.
* In case of any error, an {@link OAuth2Error} is returned in the OAuth 2.0 Error response.
*
* <p>
* By default, this {@code Filter} responds to access token requests
* By default, this {@code Filter} responds to authorization grant requests
* at the {@code URI} {@code /oauth2/token} and {@code HttpMethod} {@code POST}.
*
* <p>
@@ -81,9 +85,11 @@ import java.util.stream.Collectors;
* @author Daniel Garnier-Moiroux
* @since 0.0.1
* @see AuthenticationManager
* @see OAuth2AuthorizationCodeAuthenticationProvider
* @see OAuth2RefreshTokenAuthenticationProvider
* @see OAuth2ClientCredentialsAuthenticationProvider
* @see OAuth2AuthorizationService
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-3.2">Section 3.2 Token Endpoint</a>
*/
public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
/**
@@ -128,6 +134,7 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
this.tokenEndpointMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name());
Map<AuthorizationGrantType, Converter<HttpServletRequest, Authentication>> converters = new HashMap<>();
converters.put(AuthorizationGrantType.AUTHORIZATION_CODE, new AuthorizationCodeAuthenticationConverter());
converters.put(AuthorizationGrantType.REFRESH_TOKEN, new RefreshTokenAuthenticationConverter());
converters.put(AuthorizationGrantType.CLIENT_CREDENTIALS, new ClientCredentialsAuthenticationConverter());
this.authorizationGrantAuthenticationConverter = new DelegatingAuthorizationGrantAuthenticationConverter(converters);
}
@@ -154,7 +161,7 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication);
sendAccessTokenResponse(response, accessTokenAuthentication.getAccessToken());
sendAccessTokenResponse(response, accessTokenAuthentication.getAccessToken(), accessTokenAuthentication.getRefreshToken());
} catch (OAuth2AuthenticationException ex) {
SecurityContextHolder.clearContext();
@@ -162,7 +169,9 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
}
}
private void sendAccessTokenResponse(HttpServletResponse response, OAuth2AccessToken accessToken) throws IOException {
private void sendAccessTokenResponse(HttpServletResponse response, OAuth2AccessToken accessToken,
OAuth2RefreshToken refreshToken) throws IOException {
OAuth2AccessTokenResponse.Builder builder =
OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue())
.tokenType(accessToken.getTokenType())
@@ -170,6 +179,9 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) {
builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()));
}
if (refreshToken != null) {
builder.refreshToken(refreshToken.getTokenValue());
}
OAuth2AccessTokenResponse accessTokenResponse = builder.build();
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
this.accessTokenHttpResponseConverter.write(accessTokenResponse, null, httpResponse);
@@ -229,6 +241,43 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
}
}
private static class RefreshTokenAuthenticationConverter implements Converter<HttpServletRequest, Authentication> {
@Override
public Authentication convert(HttpServletRequest request) {
// grant_type (REQUIRED)
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
if (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) {
return null;
}
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
// refresh_token (REQUIRED)
String refreshToken = parameters.getFirst(OAuth2ParameterNames.REFRESH_TOKEN);
if (!StringUtils.hasText(refreshToken) ||
parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REFRESH_TOKEN);
}
// scope (OPTIONAL)
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
if (StringUtils.hasText(scope) &&
parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE);
}
if (StringUtils.hasText(scope)) {
Set<String> requestedScopes = new HashSet<>(
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal, requestedScopes);
}
return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal);
}
}
private static class ClientCredentialsAuthenticationConverter implements Converter<HttpServletRequest, Authentication> {
@Override

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.web;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames2;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* A {@code Filter} for the OAuth 2.0 Token Revocation endpoint.
*
* @author Vivek Babu
* @author Joe Grandja
* @see OAuth2TokenRevocationAuthenticationProvider
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2">Section 2 Token Revocation</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2.1">Section 2.1 Revocation Request</a>
* @since 0.0.3
*/
public class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFilter {
/**
* The default endpoint {@code URI} for token revocation requests.
*/
public static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke";
private final AuthenticationManager authenticationManager;
private final RequestMatcher tokenRevocationEndpointMatcher;
private final Converter<HttpServletRequest, Authentication> tokenRevocationAuthenticationConverter =
new DefaultTokenRevocationAuthenticationConverter();
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
new OAuth2ErrorHttpMessageConverter();
/**
* Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided parameters.
*
* @param authenticationManager the authentication manager
*/
public OAuth2TokenRevocationEndpointFilter(AuthenticationManager authenticationManager) {
this(authenticationManager, DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI);
}
/**
* Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided parameters.
*
* @param authenticationManager the authentication manager
* @param tokenRevocationEndpointUri the endpoint {@code URI} for token revocation requests
*/
public OAuth2TokenRevocationEndpointFilter(AuthenticationManager authenticationManager,
String tokenRevocationEndpointUri) {
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
Assert.hasText(tokenRevocationEndpointUri, "tokenRevocationEndpointUri cannot be empty");
this.authenticationManager = authenticationManager;
this.tokenRevocationEndpointMatcher = new AntPathRequestMatcher(
tokenRevocationEndpointUri, HttpMethod.POST.name());
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (!this.tokenRevocationEndpointMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
try {
this.authenticationManager.authenticate(
this.tokenRevocationAuthenticationConverter.convert(request));
response.setStatus(HttpStatus.OK.value());
} catch (OAuth2AuthenticationException ex) {
SecurityContextHolder.clearContext();
sendErrorResponse(response, ex.getError());
}
}
private void sendErrorResponse(HttpServletResponse response, OAuth2Error error) throws IOException {
ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
this.errorHttpResponseConverter.write(error, null, httpResponse);
}
private static void throwError(String errorCode, String parameterName) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Token Revocation Parameter: " + parameterName,
"https://tools.ietf.org/html/rfc7009#section-2.1");
throw new OAuth2AuthenticationException(error);
}
private static class DefaultTokenRevocationAuthenticationConverter
implements Converter<HttpServletRequest, Authentication> {
@Override
public Authentication convert(HttpServletRequest request) {
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);
// token (REQUIRED)
String token = parameters.getFirst(OAuth2ParameterNames2.TOKEN);
if (!StringUtils.hasText(token) ||
parameters.get(OAuth2ParameterNames2.TOKEN).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames2.TOKEN);
}
// token_type_hint (OPTIONAL)
String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames2.TOKEN_TYPE_HINT);
if (StringUtils.hasText(tokenTypeHint) &&
parameters.get(OAuth2ParameterNames2.TOKEN_TYPE_HINT).size() != 1) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames2.TOKEN_TYPE_HINT);
}
return new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal, tokenTypeHint);
}
}
}

View File

@@ -52,10 +52,8 @@ public class PublicClientAuthenticationConverter implements AuthenticationConver
// client_id (REQUIRED for public clients)
String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID);
if (!StringUtils.hasText(clientId)) {
return null;
}
if (parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
if (!StringUtils.hasText(clientId) ||
parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) {
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST));
}

View File

@@ -34,13 +34,13 @@ import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResp
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.test.web.servlet.MockMvc;
@@ -144,7 +144,7 @@ public class OAuth2AuthorizationCodeGrantTests {
}
@Test
public void requestWhenTokenRequestValidThenResponseIncludesCacheHeaders() throws Exception {
public void requestWhenTokenRequestValidThenReturnAccessTokenResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
@@ -153,32 +153,38 @@ public class OAuth2AuthorizationCodeGrantTests {
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(authorizationService.findByToken(
eq(authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE)),
eq(authorization.getTokens().getToken(OAuth2AuthorizationCode.class).getTokenValue()),
eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
this.mvc.perform(MockMvcRequestBuilders.post(OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI)
this.mvc.perform(post(OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI)
.params(getTokenRequestParameters(registeredClient, authorization))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")));
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.token_type").isNotEmpty())
.andExpect(jsonPath("$.expires_in").isNotEmpty())
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
.andExpect(jsonPath("$.scope").isNotEmpty());
verify(registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));
verify(authorizationService).findByToken(
eq(authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE)),
eq(authorization.getTokens().getToken(OAuth2AuthorizationCode.class).getTokenValue()),
eq(TokenType.AUTHORIZATION_CODE));
verify(authorizationService).save(any());
}
@Test
public void requestWhenPublicClientWithPkceThenReturnAccessToken() throws Exception {
public void requestWhenPublicClientWithPkceThenReturnAccessTokenResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.clientSecret(null)
.clientSettings(clientSettings -> clientSettings.requireProofKey(true))
.tokenSettings(tokenSettings -> tokenSettings.enableRefreshTokens(false))
.build();
when(registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
@@ -199,7 +205,7 @@ public class OAuth2AuthorizationCodeGrantTests {
OAuth2Authorization authorization = authorizationCaptor.getValue();
when(authorizationService.findByToken(
eq(authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE)),
eq(authorization.getTokens().getToken(OAuth2AuthorizationCode.class).getTokenValue()),
eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
@@ -208,11 +214,15 @@ public class OAuth2AuthorizationCodeGrantTests {
.param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId())
.param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty());
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.token_type").isNotEmpty())
.andExpect(jsonPath("$.expires_in").isNotEmpty())
.andExpect(jsonPath("$.refresh_token").doesNotExist())
.andExpect(jsonPath("$.scope").isNotEmpty());
verify(registeredClientRepository, times(2)).findByClientId(eq(registeredClient.getClientId()));
verify(authorizationService, times(2)).findByToken(
eq(authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE)),
eq(authorization.getTokens().getToken(OAuth2AuthorizationCode.class).getTokenValue()),
eq(TokenType.AUTHORIZATION_CODE));
verify(authorizationService, times(2)).save(any());
}
@@ -232,7 +242,7 @@ public class OAuth2AuthorizationCodeGrantTests {
OAuth2Authorization authorization) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue());
parameters.set(OAuth2ParameterNames.CODE, authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE));
parameters.set(OAuth2ParameterNames.CODE, authorization.getTokens().getToken(OAuth2AuthorizationCode.class).getTokenValue());
parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next());
return parameters;
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.crypto.keys.KeyManager;
import org.springframework.security.crypto.keys.StaticKeyGeneratingKeyManager;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for the OAuth 2.0 Refresh Token Grant.
*
* @author Alexey Nesterov
* @since 0.0.3
*/
public class OAuth2RefreshTokenGrantTests {
private static RegisteredClientRepository registeredClientRepository;
private static OAuth2AuthorizationService authorizationService;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Autowired
private MockMvc mvc;
@BeforeClass
public static void init() {
registeredClientRepository = mock(RegisteredClientRepository.class);
authorizationService = mock(OAuth2AuthorizationService.class);
}
@Before
public void setup() {
reset(registeredClientRepository);
reset(authorizationService);
}
@Test
public void requestWhenRefreshTokenRequestValidThenReturnAccessTokenResponse() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
this.mvc.perform(post(OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI)
.params(getRefreshTokenRequestParameters(authorization))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store")))
.andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache")))
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.token_type").isNotEmpty())
.andExpect(jsonPath("$.expires_in").isNotEmpty())
.andExpect(jsonPath("$.refresh_token").isNotEmpty())
.andExpect(jsonPath("$.scope").isNotEmpty());
verify(registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));
verify(authorizationService).findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN));
verify(authorizationService).save(any());
}
private static MultiValueMap<String, String> getRefreshTokenRequestParameters(OAuth2Authorization authorization) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue());
parameters.set(OAuth2ParameterNames.REFRESH_TOKEN, authorization.getTokens().getRefreshToken().getTokenValue());
return parameters;
}
private static String encodeBasicAuth(String clientId, String secret) throws Exception {
clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());
secret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());
String credentialsString = clientId + ":" + secret;
byte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));
return new String(encodedBytes, StandardCharsets.UTF_8);
}
@EnableWebSecurity
@Import(OAuth2AuthorizationServerConfiguration.class)
static class AuthorizationServerConfiguration {
@Bean
RegisteredClientRepository registeredClientRepository() {
return registeredClientRepository;
}
@Bean
OAuth2AuthorizationService authorizationService() {
return authorizationService;
}
@Bean
KeyManager keyManager() { return new StaticKeyGeneratingKeyManager(); }
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.crypto.keys.KeyManager;
import org.springframework.security.crypto.keys.StaticKeyGeneratingKeyManager;
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames2;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration tests for the OAuth 2.0 Token Revocation endpoint.
*
* @author Joe Grandja
*/
public class OAuth2TokenRevocationTests {
private static RegisteredClientRepository registeredClientRepository;
private static OAuth2AuthorizationService authorizationService;
private static KeyManager keyManager;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Autowired
private MockMvc mvc;
@BeforeClass
public static void init() {
registeredClientRepository = mock(RegisteredClientRepository.class);
authorizationService = mock(OAuth2AuthorizationService.class);
keyManager = new StaticKeyGeneratingKeyManager();
}
@Before
public void setup() {
reset(registeredClientRepository);
reset(authorizationService);
}
@Test
public void requestWhenRevokeRefreshTokenThenRevoked() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
OAuth2RefreshToken token = authorization.getTokens().getRefreshToken();
TokenType tokenType = TokenType.REFRESH_TOKEN;
when(authorizationService.findByToken(eq(token.getTokenValue()), eq(tokenType))).thenReturn(authorization);
this.mvc.perform(MockMvcRequestBuilders.post(OAuth2TokenRevocationEndpointFilter.DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)
.params(getTokenRevocationRequestParameters(token, tokenType))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk());
verify(registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));
verify(authorizationService).findByToken(eq(token.getTokenValue()), eq(tokenType));
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
OAuth2RefreshToken refreshToken = updatedAuthorization.getTokens().getRefreshToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(refreshToken).isInvalidated()).isTrue();
OAuth2AccessToken accessToken = updatedAuthorization.getTokens().getAccessToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(accessToken).isInvalidated()).isTrue();
}
@Test
public void requestWhenRevokeAccessTokenThenRevoked() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
OAuth2AccessToken token = authorization.getTokens().getAccessToken();
TokenType tokenType = TokenType.ACCESS_TOKEN;
when(authorizationService.findByToken(eq(token.getTokenValue()), eq(tokenType))).thenReturn(authorization);
this.mvc.perform(MockMvcRequestBuilders.post(OAuth2TokenRevocationEndpointFilter.DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI)
.params(getTokenRevocationRequestParameters(token, tokenType))
.header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth(
registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk());
verify(registeredClientRepository).findByClientId(eq(registeredClient.getClientId()));
verify(authorizationService).findByToken(eq(token.getTokenValue()), eq(tokenType));
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
OAuth2AccessToken accessToken = updatedAuthorization.getTokens().getAccessToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(accessToken).isInvalidated()).isTrue();
OAuth2RefreshToken refreshToken = updatedAuthorization.getTokens().getRefreshToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(refreshToken).isInvalidated()).isFalse();
}
private static MultiValueMap<String, String> getTokenRevocationRequestParameters(AbstractOAuth2Token token, TokenType tokenType) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames2.TOKEN, token.getTokenValue());
parameters.set(OAuth2ParameterNames2.TOKEN_TYPE_HINT, tokenType.getValue());
return parameters;
}
private static String encodeBasicAuth(String clientId, String secret) throws Exception {
clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8.name());
secret = URLEncoder.encode(secret, StandardCharsets.UTF_8.name());
String credentialsString = clientId + ":" + secret;
byte[] encodedBytes = Base64.getEncoder().encode(credentialsString.getBytes(StandardCharsets.UTF_8));
return new String(encodedBytes, StandardCharsets.UTF_8);
}
@EnableWebSecurity
@Import(OAuth2AuthorizationServerConfiguration.class)
static class AuthorizationServerConfiguration {
@Bean
RegisteredClientRepository registeredClientRepository() {
return registeredClientRepository;
}
@Bean
OAuth2AuthorizationService authorizationService() {
return authorizationService;
}
@Bean
KeyManager keyManager() {
return keyManager;
}
}
}

View File

@@ -18,10 +18,14 @@ package org.springframework.security.oauth2.server.authorization;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -35,7 +39,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class InMemoryOAuth2AuthorizationServiceTests {
private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build();
private static final String PRINCIPAL_NAME = "principal";
private static final String AUTHORIZATION_CODE = "code";
private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode(
"code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES));
private InMemoryOAuth2AuthorizationService authorizationService;
@Before
@@ -54,12 +59,12 @@ public class InMemoryOAuth2AuthorizationServiceTests {
public void saveWhenAuthorizationProvidedThenSaved() {
OAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
.tokens(OAuth2Tokens.builder().token(AUTHORIZATION_CODE).build())
.build();
this.authorizationService.save(expectedAuthorization);
OAuth2Authorization authorization = this.authorizationService.findByToken(
AUTHORIZATION_CODE, TokenType.AUTHORIZATION_CODE);
AUTHORIZATION_CODE.getTokenValue(), TokenType.AUTHORIZATION_CODE);
assertThat(authorization).isEqualTo(expectedAuthorization);
}
@@ -74,17 +79,17 @@ public class InMemoryOAuth2AuthorizationServiceTests {
public void removeWhenAuthorizationProvidedThenRemoved() {
OAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
.tokens(OAuth2Tokens.builder().token(AUTHORIZATION_CODE).build())
.build();
this.authorizationService.save(expectedAuthorization);
OAuth2Authorization authorization = this.authorizationService.findByToken(
AUTHORIZATION_CODE, TokenType.AUTHORIZATION_CODE);
AUTHORIZATION_CODE.getTokenValue(), TokenType.AUTHORIZATION_CODE);
assertThat(authorization).isEqualTo(expectedAuthorization);
this.authorizationService.remove(expectedAuthorization);
authorization = this.authorizationService.findByToken(
AUTHORIZATION_CODE, TokenType.AUTHORIZATION_CODE);
AUTHORIZATION_CODE.getTokenValue(), TokenType.AUTHORIZATION_CODE);
assertThat(authorization).isNull();
}
@@ -113,12 +118,12 @@ public class InMemoryOAuth2AuthorizationServiceTests {
public void findByTokenWhenTokenTypeAuthorizationCodeThenFound() {
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
.tokens(OAuth2Tokens.builder().token(AUTHORIZATION_CODE).build())
.build();
this.authorizationService.save(authorization);
OAuth2Authorization result = this.authorizationService.findByToken(
AUTHORIZATION_CODE, TokenType.AUTHORIZATION_CODE);
AUTHORIZATION_CODE.getTokenValue(), TokenType.AUTHORIZATION_CODE);
assertThat(authorization).isEqualTo(result);
}
@@ -128,8 +133,7 @@ public class InMemoryOAuth2AuthorizationServiceTests {
"access-token", Instant.now().minusSeconds(60), Instant.now());
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
.accessToken(accessToken)
.tokens(OAuth2Tokens.builder().token(AUTHORIZATION_CODE).accessToken(accessToken).build())
.build();
this.authorizationService.save(authorization);
@@ -138,6 +142,20 @@ public class InMemoryOAuth2AuthorizationServiceTests {
assertThat(authorization).isEqualTo(result);
}
@Test
public void findByTokenWhenTokenTypeRefreshTokenThenFound() {
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now());
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.tokens(OAuth2Tokens.builder().refreshToken(refreshToken).build())
.build();
this.authorizationService.save(authorization);
OAuth2Authorization result = this.authorizationService.findByToken(
refreshToken.getTokenValue(), TokenType.REFRESH_TOKEN);
assertThat(authorization).isEqualTo(result);
}
@Test
public void findByTokenWhenTokenDoesNotExistThenNull() {
OAuth2Authorization result = this.authorizationService.findByToken(

View File

@@ -17,14 +17,17 @@ package org.springframework.security.oauth2.server.authorization;
import org.junit.Test;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.data.MapEntry.entry;
/**
* Tests for {@link OAuth2Authorization}.
@@ -37,7 +40,9 @@ public class OAuth2AuthorizationTests {
private static final String PRINCIPAL_NAME = "principal";
private static final OAuth2AccessToken ACCESS_TOKEN = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(), Instant.now().plusSeconds(300));
private static final String AUTHORIZATION_CODE = "code";
private static final OAuth2RefreshToken REFRESH_TOKEN = new OAuth2RefreshToken("refresh-token", Instant.now());
private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode(
"code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES));
@Test
public void withRegisteredClientWhenRegisteredClientNullThenThrowIllegalArgumentException() {
@@ -57,14 +62,15 @@ public class OAuth2AuthorizationTests {
public void fromWhenAuthorizationProvidedThenCopied() {
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.accessToken(ACCESS_TOKEN)
.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
.tokens(OAuth2Tokens.builder().token(AUTHORIZATION_CODE).accessToken(ACCESS_TOKEN).build())
.build();
OAuth2Authorization authorizationResult = OAuth2Authorization.from(authorization).build();
assertThat(authorizationResult.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId());
assertThat(authorizationResult.getPrincipalName()).isEqualTo(authorization.getPrincipalName());
assertThat(authorizationResult.getAccessToken()).isEqualTo(authorization.getAccessToken());
assertThat(authorizationResult.getTokens().getAccessToken()).isEqualTo(authorization.getTokens().getAccessToken());
assertThat(authorizationResult.getTokens().getToken(OAuth2AuthorizationCode.class))
.isEqualTo(authorization.getTokens().getToken(OAuth2AuthorizationCode.class));
assertThat(authorizationResult.getAttributes()).isEqualTo(authorization.getAttributes());
}
@@ -97,14 +103,13 @@ public class OAuth2AuthorizationTests {
public void buildWhenAllAttributesAreProvidedThenAllAttributesAreSet() {
OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT)
.principalName(PRINCIPAL_NAME)
.accessToken(ACCESS_TOKEN)
.attribute(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE)
.tokens(OAuth2Tokens.builder().token(AUTHORIZATION_CODE).accessToken(ACCESS_TOKEN).refreshToken(REFRESH_TOKEN).build())
.build();
assertThat(authorization.getRegisteredClientId()).isEqualTo(REGISTERED_CLIENT.getId());
assertThat(authorization.getPrincipalName()).isEqualTo(PRINCIPAL_NAME);
assertThat(authorization.getAccessToken()).isEqualTo(ACCESS_TOKEN);
assertThat(authorization.getAttributes()).containsExactly(
entry(OAuth2AuthorizationAttributeNames.CODE, AUTHORIZATION_CODE));
assertThat(authorization.getTokens().getToken(OAuth2AuthorizationCode.class)).isEqualTo(AUTHORIZATION_CODE);
assertThat(authorization.getTokens().getAccessToken()).isEqualTo(ACCESS_TOKEN);
assertThat(authorization.getTokens().getRefreshToken()).isEqualTo(REFRESH_TOKEN);
}
}

View File

@@ -16,11 +16,16 @@
package org.springframework.security.oauth2.server.authorization;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken2;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Map;
@@ -40,8 +45,12 @@ public class TestOAuth2Authorizations {
public static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient,
Map<String, Object> authorizationRequestAdditionalParameters) {
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(
"code", Instant.now(), Instant.now().plusSeconds(120));
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(), Instant.now().plusSeconds(300));
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken2(
"refresh-token", Instant.now(), Instant.now().plus(1, ChronoUnit.HOURS));
OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()
.authorizationUri("https://provider.com/oauth2/authorize")
.clientId(registeredClient.getClientId())
@@ -52,8 +61,7 @@ public class TestOAuth2Authorizations {
.build();
return OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName("principal")
.accessToken(accessToken)
.attribute(OAuth2AuthorizationAttributeNames.CODE, "code")
.tokens(OAuth2Tokens.builder().token(authorizationCode).accessToken(accessToken).refreshToken(refreshToken).build())
.attribute(OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST, authorizationRequest)
.attribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES, authorizationRequest.getScopes());
}

View File

@@ -33,11 +33,14 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenMetadata;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Set;
@@ -58,7 +61,6 @@ import static org.mockito.Mockito.when;
*/
public class OAuth2AuthorizationCodeAuthenticationProviderTests {
private static final String AUTHORIZATION_CODE = "code";
private RegisteredClient registeredClient;
private RegisteredClientRepository registeredClientRepository;
private OAuth2AuthorizationService authorizationService;
private JwtEncoder jwtEncoder;
@@ -66,8 +68,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
@Before
public void setUp() {
this.registeredClient = TestRegisteredClients.registeredClient().build();
this.registeredClientRepository = new InMemoryRegisteredClientRepository(this.registeredClient);
this.registeredClientRepository = mock(RegisteredClientRepository.class);
this.authorizationService = mock(OAuth2AuthorizationService.class);
this.jwtEncoder = mock(JwtEncoder.class);
this.authenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
@@ -102,8 +103,9 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
@Test
public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(
this.registeredClient.getClientId(), this.registeredClient.getClientSecret());
registeredClient.getClientId(), registeredClient.getClientSecret());
OAuth2AuthorizationCodeAuthenticationToken authentication =
new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -115,8 +117,9 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
@Test
public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
this.registeredClient.getClientId(), this.registeredClient.getClientSecret(), null);
registeredClient.getClientId(), registeredClient.getClientSecret(), null);
OAuth2AuthorizationCodeAuthenticationToken authentication =
new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -128,7 +131,8 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
@Test
public void authenticateWhenInvalidCodeThenThrowOAuth2AuthenticationException() {
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AuthorizationCodeAuthenticationToken authentication =
new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -153,15 +157,22 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
OAuth2AuthorizationCode authorizationCode = updatedAuthorization.getTokens().getToken(OAuth2AuthorizationCode.class);
assertThat(updatedAuthorization.getTokens().getTokenMetadata(authorizationCode).isInvalidated()).isTrue();
}
@Test
public void authenticateWhenInvalidRedirectUriThenThrowOAuth2AuthenticationException() {
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
OAuth2AuthorizationCodeAuthenticationToken authentication =
@@ -174,12 +185,39 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
}
@Test
public void authenticateWhenValidCodeThenReturnAccessToken() {
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build();
public void authenticateWhenInvalidatedCodeThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(
AUTHORIZATION_CODE, Instant.now(), Instant.now().plusSeconds(120));
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient)
.tokens(OAuth2Tokens.builder()
.token(authorizationCode, OAuth2TokenMetadata.builder().invalidated().build())
.build())
.build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
OAuth2AuthorizationCodeAuthenticationToken authentication =
new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
}
@Test
public void authenticateWhenValidCodeThenReturnAccessToken() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
OAuth2AuthorizationCodeAuthenticationToken authentication =
@@ -196,6 +234,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
Set<String> scopes = jwtClaimsSet.getClaim(OAuth2ParameterNames.SCOPE);
assertThat(scopes).isEqualTo(authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES));
assertThat(jwtClaimsSet.getSubject()).isEqualTo(authorization.getPrincipalName());
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
@@ -203,8 +242,67 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests {
assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId());
assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);
assertThat(updatedAuthorization.getAccessToken()).isNotNull();
assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken());
assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getTokens().getAccessToken());
assertThat(accessTokenAuthentication.getRefreshToken()).isNotNull();
assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getTokens().getRefreshToken());
OAuth2AuthorizationCode authorizationCode = updatedAuthorization.getTokens().getToken(OAuth2AuthorizationCode.class);
assertThat(updatedAuthorization.getTokens().getTokenMetadata(authorizationCode).isInvalidated()).isTrue();
}
@Test
public void authenticateWhenRefreshTokenTimeToLiveConfiguredThenRefreshTokenExpirySet() {
Duration refreshTokenTTL = Duration.ofDays(1);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.tokenSettings(tokenSettings -> tokenSettings.refreshTokenTimeToLive(refreshTokenTTL))
.build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
OAuth2AuthorizationCodeAuthenticationToken authentication =
new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);
when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt());
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getTokens().getRefreshToken());
Instant expectedRefreshTokenExpiresAt = accessTokenAuthentication.getRefreshToken().getIssuedAt().plus(refreshTokenTTL);
assertThat(accessTokenAuthentication.getRefreshToken().getExpiresAt()).isBetween(
expectedRefreshTokenExpiresAt.minusSeconds(1), expectedRefreshTokenExpiresAt.plusSeconds(1));
}
@Test
public void authenticateWhenRefreshTokenDisabledThenRefreshTokenNull() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.tokenSettings(tokenSettings -> tokenSettings.enableRefreshTokens(false))
.build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(TokenType.AUTHORIZATION_CODE)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
OAuth2AuthorizationCodeAuthenticationToken authentication =
new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null);
when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt());
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
assertThat(accessTokenAuthentication.getRefreshToken()).isNull();
}
private static Jwt createJwt() {

View File

@@ -37,7 +37,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
/**
@@ -119,6 +118,21 @@ public class OAuth2ClientAuthenticationProviderTests {
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenClientSecretNotProvidedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication =
new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), null);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenValidCredentialsThenAuthenticated() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
@@ -135,21 +149,6 @@ public class OAuth2ClientAuthenticationProviderTests {
assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient);
}
@Test
public void authenticateWhenNotPkceThenContinueAuthenticated() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId())))
.thenReturn(registeredClient);
OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), registeredClient.getClientSecret(), null);
OAuth2ClientAuthenticationToken authenticationResult =
(OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication);
assertThat(authenticationResult.isAuthenticated()).isTrue();
verifyNoInteractions(this.authorizationService);
}
@Test
public void authenticateWhenPkceAndInvalidCodeThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();

View File

@@ -19,6 +19,7 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.jose.JoseHeaderNames;
@@ -49,14 +50,12 @@ import static org.mockito.Mockito.when;
* @author Joe Grandja
*/
public class OAuth2ClientCredentialsAuthenticationProviderTests {
private RegisteredClient registeredClient;
private OAuth2AuthorizationService authorizationService;
private JwtEncoder jwtEncoder;
private OAuth2ClientCredentialsAuthenticationProvider authenticationProvider;
@Before
public void setUp() {
this.registeredClient = TestRegisteredClients.registeredClient().build();
this.authorizationService = mock(OAuth2AuthorizationService.class);
this.jwtEncoder = mock(JwtEncoder.class);
this.authenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(
@@ -89,8 +88,9 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
@Test
public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(
this.registeredClient.getClientId(), this.registeredClient.getClientSecret());
registeredClient.getClientId(), registeredClient.getClientSecret());
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -102,8 +102,9 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
@Test
public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
this.registeredClient.getClientId(), this.registeredClient.getClientSecret(), null);
registeredClient.getClientId(), registeredClient.getClientSecret(), null);
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
@@ -113,9 +114,25 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenClientNotAuthorizedToRequestTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2()
.authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.CLIENT_CREDENTIALS))
.build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
@Test
public void authenticateWhenInvalidScopeThenThrowOAuth2AuthenticationException() {
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(
clientPrincipal, Collections.singleton("invalid-scope"));
@@ -128,7 +145,8 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
@Test
public void authenticateWhenScopeRequestedThenAccessTokenContainsScope() {
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
Set<String> requestedScope = Collections.singleton("openid");
OAuth2ClientCredentialsAuthenticationToken authentication =
new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScope);
@@ -142,7 +160,8 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
@Test
public void authenticateWhenValidAuthenticationThenReturnAccessToken() {
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient);
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal);
when(this.jwtEncoder.encode(any(), any())).thenReturn(createJwt());
@@ -156,10 +175,10 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
assertThat(authorization.getRegisteredClientId()).isEqualTo(clientPrincipal.getRegisteredClient().getId());
assertThat(authorization.getPrincipalName()).isEqualTo(clientPrincipal.getName());
assertThat(authorization.getAccessToken()).isNotNull();
assertThat(authorization.getAccessToken().getScopes()).isEqualTo(clientPrincipal.getRegisteredClient().getScopes());
assertThat(authorization.getTokens().getAccessToken()).isNotNull();
assertThat(authorization.getTokens().getAccessToken().getScopes()).isEqualTo(clientPrincipal.getRegisteredClient().getScopes());
assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);
assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(authorization.getAccessToken());
assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(authorization.getTokens().getAccessToken());
}
private static Jwt createJwt() {

View File

@@ -0,0 +1,314 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken2;
import org.springframework.security.oauth2.jose.JoseHeaderNames;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationAttributeNames;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2Tokens;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.Set;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link OAuth2RefreshTokenAuthenticationProvider}.
*
* @author Alexey Nesterov
* @since 0.0.3
*/
public class OAuth2RefreshTokenAuthenticationProviderTests {
private OAuth2AuthorizationService authorizationService;
private JwtEncoder jwtEncoder;
private OAuth2RefreshTokenAuthenticationProvider authenticationProvider;
@Before
public void setUp() {
this.authorizationService = mock(OAuth2AuthorizationService.class);
this.jwtEncoder = mock(JwtEncoder.class);
Jwt jwt = Jwt.withTokenValue("refreshed-access-token")
.header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName())
.issuedAt(Instant.now())
.expiresAt(Instant.now().plus(1, ChronoUnit.HOURS))
.build();
when(this.jwtEncoder.encode(any(), any())).thenReturn(jwt);
this.authenticationProvider = new OAuth2RefreshTokenAuthenticationProvider(
this.authorizationService, this.jwtEncoder);
}
@Test
public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationProvider(null, this.jwtEncoder))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("authorizationService cannot be null");
}
@Test
public void constructorWhenJwtEncoderNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationProvider(this.authorizationService, null))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("jwtEncoder cannot be null");
}
@Test
public void supportsWhenSupportedAuthenticationThenTrue() {
assertThat(this.authenticationProvider.supports(OAuth2RefreshTokenAuthenticationToken.class)).isTrue();
}
@Test
public void supportsWhenUnsupportedAuthenticationThenFalse() {
assertThat(this.authenticationProvider.supports(OAuth2ClientCredentialsAuthenticationToken.class)).isFalse();
}
@Test
public void authenticateWhenValidRefreshTokenThenReturnAccessToken() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal);
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId());
assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal);
assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getTokens().getAccessToken());
assertThat(updatedAuthorization.getTokens().getAccessToken()).isNotEqualTo(authorization.getTokens().getAccessToken());
assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getTokens().getRefreshToken());
// By default, refresh token is reused
assertThat(updatedAuthorization.getTokens().getRefreshToken()).isEqualTo(authorization.getTokens().getRefreshToken());
}
@Test
public void authenticateWhenReuseRefreshTokensFalseThenReturnNewRefreshToken() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.tokenSettings(tokenSettings -> tokenSettings.reuseRefreshTokens(false))
.build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal);
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getTokens().getRefreshToken());
assertThat(updatedAuthorization.getTokens().getRefreshToken()).isNotEqualTo(authorization.getTokens().getRefreshToken());
}
@Test
public void authenticateWhenRequestedScopesAuthorizedThenAccessTokenIncludesScopes() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
Set<String> requestedScopes = new HashSet<>(authorizedScopes);
requestedScopes.remove("email");
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal, requestedScopes);
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
(OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication);
assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScopes);
}
@Test
public void authenticateWhenRequestedScopesNotAuthorizedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
Set<String> authorizedScopes = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES);
Set<String> requestedScopes = new HashSet<>(authorizedScopes);
requestedScopes.add("unauthorized");
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal, requestedScopes);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE);
}
@Test
public void authenticateWhenInvalidRefreshTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
"invalid", clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
}
@Test
public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(
registeredClient.getClientId(), registeredClient.getClientSecret());
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
"refresh-token", clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), registeredClient.getClientSecret(), null);
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
"refresh-token", clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenRefreshTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
TestRegisteredClients.registeredClient2().build());
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenClientNotAuthorizedToRefreshTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient()
.authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN))
.build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
@Test
public void authenticateWhenExpiredRefreshTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build();
OAuth2RefreshToken expiredRefreshToken = new OAuth2RefreshToken2(
"expired-refresh-token", Instant.now().minusSeconds(120), Instant.now().minusSeconds(60));
OAuth2Tokens tokens = OAuth2Tokens.from(authorization.getTokens()).refreshToken(expiredRefreshToken).build();
authorization = OAuth2Authorization.from(authorization).tokens(tokens).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_GRANT);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.junit.Test;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link OAuth2RefreshTokenAuthenticationToken}.
*
* @author Alexey Nesterov
* @since 0.0.3
*/
public class OAuth2RefreshTokenAuthenticationTokenTests {
private final OAuth2ClientAuthenticationToken clientPrincipal =
new OAuth2ClientAuthenticationToken(TestRegisteredClients.registeredClient().build());
@Test
public void constructorWhenRefreshTokenNullOrEmptyThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, this.clientPrincipal))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("refreshToken cannot be empty");
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", this.clientPrincipal))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("refreshToken cannot be empty");
}
@Test
public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("refresh-token", null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("clientPrincipal cannot be null");
}
@Test
public void constructorWhenScopesNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("refresh-token", this.clientPrincipal, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("scopes cannot be null");
}
@Test
public void constructorWhenScopesProvidedThenCreated() {
Set<String> expectedScopes = new HashSet<>(Arrays.asList("scope-a", "scope-b"));
OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken(
"refresh-token", this.clientPrincipal, expectedScopes);
assertThat(authentication.getRefreshToken()).isEqualTo("refresh-token");
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);
assertThat(authentication.getCredentials().toString()).isEmpty();
assertThat(authentication.getScopes()).isEqualTo(expectedScopes);
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes2;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link OAuth2TokenRevocationAuthenticationProvider}.
*
* @author Vivek Babu
* @author Joe Grandja
*/
public class OAuth2TokenRevocationAuthenticationProviderTests {
private OAuth2AuthorizationService authorizationService;
private OAuth2TokenRevocationAuthenticationProvider authenticationProvider;
@Before
public void setUp() {
this.authorizationService = mock(OAuth2AuthorizationService.class);
this.authenticationProvider = new OAuth2TokenRevocationAuthenticationProvider(this.authorizationService);
}
@Test
public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationProvider(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authorizationService cannot be null");
}
@Test
public void supportsWhenTypeOAuth2TokenRevocationAuthenticationTokenThenReturnTrue() {
assertThat(this.authenticationProvider.supports(OAuth2TokenRevocationAuthenticationToken.class)).isTrue();
}
@Test
public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(
registeredClient.getClientId(), registeredClient.getClientSecret());
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
"token", clientPrincipal, TokenType.ACCESS_TOKEN.getValue());
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
registeredClient.getClientId(), registeredClient.getClientSecret(), null);
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
"token", clientPrincipal, TokenType.ACCESS_TOKEN.getValue());
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenInvalidTokenTypeThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
"token", clientPrincipal, OAuth2ErrorCodes2.UNSUPPORTED_TOKEN_TYPE);
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes2.UNSUPPORTED_TOKEN_TYPE);
}
@Test
public void authenticateWhenInvalidTokenThenNotRevoked() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
"token", clientPrincipal, TokenType.ACCESS_TOKEN.getValue());
OAuth2TokenRevocationAuthenticationToken authenticationResult =
(OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider.authenticate(authentication);
assertThat(authenticationResult.isAuthenticated()).isFalse();
verify(this.authorizationService, never()).save(any());
}
@Test
public void authenticateWhenTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
TestRegisteredClients.registeredClient2().build()).build();
when(this.authorizationService.findByToken(
eq("token"),
eq(TokenType.ACCESS_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
"token", clientPrincipal, TokenType.ACCESS_TOKEN.getValue());
assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT);
}
@Test
public void authenticateWhenValidRefreshTokenThenRevoked() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getRefreshToken().getTokenValue()),
eq(TokenType.REFRESH_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
authorization.getTokens().getRefreshToken().getTokenValue(), clientPrincipal, TokenType.REFRESH_TOKEN.getValue());
OAuth2TokenRevocationAuthenticationToken authenticationResult =
(OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider.authenticate(authentication);
assertThat(authenticationResult.isAuthenticated()).isTrue();
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
OAuth2RefreshToken refreshToken = updatedAuthorization.getTokens().getRefreshToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(refreshToken).isInvalidated()).isTrue();
OAuth2AccessToken accessToken = updatedAuthorization.getTokens().getAccessToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(accessToken).isInvalidated()).isTrue();
}
@Test
public void authenticateWhenValidAccessTokenThenRevoked() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(
registeredClient).build();
when(this.authorizationService.findByToken(
eq(authorization.getTokens().getAccessToken().getTokenValue()),
eq(TokenType.ACCESS_TOKEN)))
.thenReturn(authorization);
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
authorization.getTokens().getAccessToken().getTokenValue(), clientPrincipal, TokenType.ACCESS_TOKEN.getValue());
OAuth2TokenRevocationAuthenticationToken authenticationResult =
(OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider.authenticate(authentication);
assertThat(authenticationResult.isAuthenticated()).isTrue();
ArgumentCaptor<OAuth2Authorization> authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class);
verify(this.authorizationService).save(authorizationCaptor.capture());
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
OAuth2AccessToken accessToken = updatedAuthorization.getTokens().getAccessToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(accessToken).isInvalidated()).isTrue();
OAuth2RefreshToken refreshToken = updatedAuthorization.getTokens().getRefreshToken();
assertThat(updatedAuthorization.getTokens().getTokenMetadata(refreshToken).isInvalidated()).isFalse();
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import org.junit.Test;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import java.time.Duration;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link OAuth2TokenRevocationAuthenticationToken}.
*
* @author Vivek Babu
* @author Joe Grandja
*/
public class OAuth2TokenRevocationAuthenticationTokenTests {
private String token = "token";
private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(
TestRegisteredClients.registeredClient().build());
private String tokenTypeHint = TokenType.ACCESS_TOKEN.getValue();
private OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, this.token,
Instant.now(), Instant.now().plus(Duration.ofHours(1)));
@Test
public void constructorWhenTokenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal, this.tokenTypeHint))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("token cannot be empty");
}
@Test
public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(this.token, null, this.tokenTypeHint))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("clientPrincipal cannot be null");
}
@Test
public void constructorWhenRevokedTokenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("revokedToken cannot be null");
}
@Test
public void constructorWhenRevokedTokenAndClientPrincipalNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(this.accessToken, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("clientPrincipal cannot be null");
}
@Test
public void constructorWhenTokenProvidedThenCreated() {
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
this.token, this.clientPrincipal, this.tokenTypeHint);
assertThat(authentication.getToken()).isEqualTo(this.token);
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);
assertThat(authentication.getTokenTypeHint()).isEqualTo(this.tokenTypeHint);
assertThat(authentication.getCredentials().toString()).isEmpty();
assertThat(authentication.isAuthenticated()).isFalse();
}
@Test
public void constructorWhenRevokedTokenProvidedThenCreated() {
OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken(
this.accessToken, this.clientPrincipal);
assertThat(authentication.getToken()).isEqualTo(this.accessToken.getTokenValue());
assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal);
assertThat(authentication.getTokenTypeHint()).isNull();
assertThat(authentication.getCredentials().toString()).isEmpty();
assertThat(authentication.isAuthenticated()).isTrue();
}
}

View File

@@ -28,6 +28,7 @@ public class TestRegisteredClients {
.clientId("client-1")
.clientSecret("secret")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.redirectUri("https://example.com")
.scope("openid")
@@ -40,6 +41,7 @@ public class TestRegisteredClients {
.clientId("client-2")
.clientSecret("secret")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientAuthenticationMethod(ClientAuthenticationMethod.BASIC)
.redirectUri("https://example.com")

View File

@@ -32,8 +32,11 @@ public class TokenSettingsTests {
@Test
public void constructorWhenDefaultThenDefaultsAreSet() {
TokenSettings tokenSettings = new TokenSettings();
assertThat(tokenSettings.settings()).hasSize(1);
assertThat(tokenSettings.settings()).hasSize(4);
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
assertThat(tokenSettings.enableRefreshTokens()).isTrue();
assertThat(tokenSettings.reuseRefreshTokens()).isTrue();
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
}
@Test
@@ -50,6 +53,61 @@ public class TokenSettingsTests {
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);
}
@Test
public void accessTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new TokenSettings().accessTokenTimeToLive(null))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("accessTokenTimeToLive cannot be null");
assertThatThrownBy(() -> new TokenSettings().accessTokenTimeToLive(Duration.ZERO))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("accessTokenTimeToLive must be greater than Duration.ZERO");
assertThatThrownBy(() -> new TokenSettings().accessTokenTimeToLive(Duration.ofSeconds(-10)))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("accessTokenTimeToLive must be greater than Duration.ZERO");
}
@Test
public void enableRefreshTokensWhenFalseThenSet() {
TokenSettings tokenSettings = new TokenSettings().enableRefreshTokens(false);
assertThat(tokenSettings.enableRefreshTokens()).isFalse();
}
@Test
public void reuseRefreshTokensWhenFalseThenSet() {
TokenSettings tokenSettings = new TokenSettings().reuseRefreshTokens(false);
assertThat(tokenSettings.reuseRefreshTokens()).isFalse();
}
@Test
public void refreshTokenTimeToLiveWhenProvidedThenSet() {
Duration refreshTokenTimeToLive = Duration.ofDays(10);
TokenSettings tokenSettings = new TokenSettings().refreshTokenTimeToLive(refreshTokenTimeToLive);
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(refreshTokenTimeToLive);
}
@Test
public void refreshTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(null))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("refreshTokenTimeToLive cannot be null");
assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ZERO))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO");
assertThatThrownBy(() -> new TokenSettings().refreshTokenTimeToLive(Duration.ofSeconds(-10)))
.isInstanceOf(IllegalArgumentException.class)
.extracting(Throwable::getMessage)
.isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO");
}
@Test
public void settingWhenCalledThenReturnTokenSettings() {
Duration accessTokenTimeToLive = Duration.ofMinutes(10);
@@ -57,8 +115,11 @@ public class TokenSettingsTests {
.<TokenSettings>setting("name1", "value1")
.accessTokenTimeToLive(accessTokenTimeToLive)
.<TokenSettings>settings(settings -> settings.put("name2", "value2"));
assertThat(tokenSettings.settings()).hasSize(3);
assertThat(tokenSettings.settings()).hasSize(6);
assertThat(tokenSettings.accessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);
assertThat(tokenSettings.enableRefreshTokens()).isTrue();
assertThat(tokenSettings.reuseRefreshTokens()).isTrue();
assertThat(tokenSettings.refreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
assertThat(tokenSettings.<String>setting("name1")).isEqualTo("value1");
assertThat(tokenSettings.<String>setting("name2")).isEqualTo("value2");
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link OAuth2TokenMetadata}.
*
* @author Joe Grandja
*/
public class OAuth2TokenMetadataTests {
@Test
public void metadataWhenNameNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() ->
OAuth2TokenMetadata.builder()
.metadata(null, "value"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("name cannot be empty");
}
@Test
public void metadataWhenValueNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() ->
OAuth2TokenMetadata.builder()
.metadata("name", null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("value cannot be null");
}
@Test
public void getMetadataWhenNameNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2TokenMetadata.builder().build().getMetadata(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("name cannot be empty");
}
@Test
public void buildWhenDefaultThenDefaultsAreSet() {
OAuth2TokenMetadata tokenMetadata = OAuth2TokenMetadata.builder().build();
assertThat(tokenMetadata.getMetadata()).hasSize(1);
assertThat(tokenMetadata.isInvalidated()).isFalse();
}
@Test
public void buildWhenMetadataProvidedThenMetadataIsSet() {
OAuth2TokenMetadata tokenMetadata = OAuth2TokenMetadata.builder()
.invalidated()
.metadata("name1", "value1")
.metadata(metadata -> metadata.put("name2", "value2"))
.build();
assertThat(tokenMetadata.getMetadata()).hasSize(3);
assertThat(tokenMetadata.isInvalidated()).isTrue();
assertThat(tokenMetadata.<String>getMetadata("name1")).isEqualTo("value1");
assertThat(tokenMetadata.<String>getMetadata("name2")).isEqualTo("value2");
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.token;
import org.junit.Before;
import org.junit.Test;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Tests for {@link OAuth2Tokens}.
*
* @author Joe Grandja
*/
public class OAuth2TokensTests {
private OAuth2AccessToken accessToken;
private OAuth2RefreshToken refreshToken;
private OidcIdToken idToken;
@Before
public void setUp() {
Instant issuedAt = Instant.now();
this.accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER,
"access-token",
issuedAt,
issuedAt.plus(Duration.ofMinutes(5)),
new HashSet<>(Arrays.asList("read", "write")));
this.refreshToken = new OAuth2RefreshToken(
"refresh-token",
issuedAt);
this.idToken = OidcIdToken.withTokenValue("id-token")
.issuer("https://provider.com")
.subject("subject")
.issuedAt(issuedAt)
.expiresAt(issuedAt.plus(Duration.ofMinutes(30)))
.build();
}
@Test
public void accessTokenWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.builder().accessToken(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("token cannot be null");
}
@Test
public void refreshTokenWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.builder().refreshToken(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("token cannot be null");
}
@Test
public void tokenWhenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.builder().token(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("token cannot be null");
}
@Test
public void getTokenWhenTokenTypeNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.builder().build().getToken((Class<OAuth2AccessToken>) null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("tokenType cannot be null");
}
@Test
public void getTokenWhenTokenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.builder().build().getToken((String) null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("token cannot be empty");
}
@Test
public void getTokenMetadataWhenTokenNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.builder().build().getTokenMetadata(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("token cannot be null");
}
@Test
public void fromWhenTokensNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> OAuth2Tokens.from(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("tokens cannot be null");
}
@Test
public void fromWhenTokensProvidedThenCopied() {
OAuth2Tokens tokens = OAuth2Tokens.builder()
.accessToken(this.accessToken)
.refreshToken(this.refreshToken)
.token(this.idToken)
.build();
OAuth2Tokens tokensResult = OAuth2Tokens.from(tokens).build();
assertThat(tokensResult.getAccessToken()).isEqualTo(tokens.getAccessToken());
assertThat(tokensResult.getTokenMetadata(tokensResult.getAccessToken()))
.isEqualTo(tokens.getTokenMetadata(tokens.getAccessToken()));
assertThat(tokensResult.getRefreshToken()).isEqualTo(tokens.getRefreshToken());
assertThat(tokensResult.getTokenMetadata(tokensResult.getRefreshToken()))
.isEqualTo(tokens.getTokenMetadata(tokens.getRefreshToken()));
assertThat(tokensResult.getToken(OidcIdToken.class)).isEqualTo(tokens.getToken(OidcIdToken.class));
assertThat(tokensResult.getTokenMetadata(tokensResult.getToken(OidcIdToken.class)))
.isEqualTo(tokens.getTokenMetadata(tokens.getToken(OidcIdToken.class)));
}
@Test
public void buildWhenTokenMetadataNotProvidedThenDefaultsAreSet() {
OAuth2Tokens tokens = OAuth2Tokens.builder()
.accessToken(this.accessToken)
.refreshToken(this.refreshToken)
.token(this.idToken)
.build();
assertThat(tokens.getAccessToken()).isEqualTo(this.accessToken);
OAuth2TokenMetadata tokenMetadata = tokens.getTokenMetadata(tokens.getAccessToken());
assertThat(tokenMetadata.isInvalidated()).isFalse();
assertThat(tokens.getRefreshToken()).isEqualTo(this.refreshToken);
tokenMetadata = tokens.getTokenMetadata(tokens.getRefreshToken());
assertThat(tokenMetadata.isInvalidated()).isFalse();
assertThat(tokens.getToken(OidcIdToken.class)).isEqualTo(this.idToken);
tokenMetadata = tokens.getTokenMetadata(tokens.getToken(OidcIdToken.class));
assertThat(tokenMetadata.isInvalidated()).isFalse();
}
@Test
public void buildWhenTokenMetadataProvidedThenTokenMetadataIsSet() {
OAuth2TokenMetadata expectedTokenMetadata = OAuth2TokenMetadata.builder().build();
OAuth2Tokens tokens = OAuth2Tokens.builder()
.accessToken(this.accessToken, expectedTokenMetadata)
.refreshToken(this.refreshToken, expectedTokenMetadata)
.token(this.idToken, expectedTokenMetadata)
.build();
assertThat(tokens.getAccessToken()).isEqualTo(this.accessToken);
OAuth2TokenMetadata tokenMetadata = tokens.getTokenMetadata(tokens.getAccessToken());
assertThat(tokenMetadata).isEqualTo(expectedTokenMetadata);
assertThat(tokens.getRefreshToken()).isEqualTo(this.refreshToken);
tokenMetadata = tokens.getTokenMetadata(tokens.getRefreshToken());
assertThat(tokenMetadata).isEqualTo(expectedTokenMetadata);
assertThat(tokens.getToken(OidcIdToken.class)).isEqualTo(this.idToken);
tokenMetadata = tokens.getTokenMetadata(tokens.getToken(OidcIdToken.class));
assertThat(tokenMetadata).isEqualTo(expectedTokenMetadata);
}
@Test
public void getTokenMetadataWhenTokenNotFoundThenNull() {
OAuth2TokenMetadata expectedTokenMetadata = OAuth2TokenMetadata.builder().build();
OAuth2Tokens tokens = OAuth2Tokens.builder()
.accessToken(this.accessToken, expectedTokenMetadata)
.build();
assertThat(tokens.getAccessToken()).isEqualTo(this.accessToken);
OAuth2TokenMetadata tokenMetadata = tokens.getTokenMetadata(tokens.getAccessToken());
assertThat(tokenMetadata).isEqualTo(expectedTokenMetadata);
OAuth2AccessToken otherAccessToken = new OAuth2AccessToken(
this.accessToken.getTokenType(),
"other-access-token",
this.accessToken.getIssuedAt(),
this.accessToken.getExpiresAt(),
this.accessToken.getScopes());
assertThat(tokens.getTokenMetadata(otherAccessToken)).isNull();
}
}

View File

@@ -40,6 +40,7 @@ import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AuthorizationCode;
import org.springframework.util.StringUtils;
import javax.servlet.FilterChain;
@@ -434,8 +435,8 @@ public class OAuth2AuthorizationEndpointFilterTests {
assertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());
assertThat(authorization.getPrincipalName()).isEqualTo(this.authentication.getPrincipal().toString());
String code = authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE);
assertThat(code).isNotNull();
OAuth2AuthorizationCode authorizationCode = authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
assertThat(authorizationCode).isNotNull();
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
assertThat(authorizationRequest).isNotNull();
@@ -481,8 +482,8 @@ public class OAuth2AuthorizationEndpointFilterTests {
assertThat(authorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());
assertThat(authorization.getPrincipalName()).isEqualTo(this.authentication.getPrincipal().toString());
String code = authorization.getAttribute(OAuth2AuthorizationAttributeNames.CODE);
assertThat(code).isNotNull();
OAuth2AuthorizationCode authorizationCode = authorization.getTokens().getToken(OAuth2AuthorizationCode.class);
assertThat(authorizationCode).isNotNull();
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST);
assertThat(authorizationRequest).isNotNull();
@@ -755,9 +756,8 @@ public class OAuth2AuthorizationEndpointFilterTests {
OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue();
assertThat(updatedAuthorization.getRegisteredClientId()).isEqualTo(registeredClient.getId());
assertThat(updatedAuthorization.getPrincipalName()).isEqualTo(this.authentication.getPrincipal().toString());
assertThat(updatedAuthorization.getAccessToken()).isNotNull();
assertThat(updatedAuthorization.getTokens().getToken(OAuth2AuthorizationCode.class)).isNotNull();
assertThat(updatedAuthorization.<String>getAttribute(OAuth2AuthorizationAttributeNames.STATE)).isNull();
assertThat(updatedAuthorization.<String>getAttribute(OAuth2AuthorizationAttributeNames.CODE)).isNotNull();
assertThat(updatedAuthorization.<OAuth2AuthorizationRequest>getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST))
.isEqualTo(authorization.<OAuth2AuthorizationRequest>getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZATION_REQUEST));
assertThat(updatedAuthorization.<Set<String>>getAttribute(OAuth2AuthorizationAttributeNames.AUTHORIZED_SCOPES))

View File

@@ -32,6 +32,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
@@ -41,6 +42,7 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import org.springframework.util.StringUtils;
@@ -248,15 +250,9 @@ public class OAuth2TokenEndpointFilterTests {
}
@Test
public void doFilterWhenTokenRequestMultipleScopeThenInvalidRequestError() throws Exception {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(clientPrincipal);
SecurityContextHolder.setContext(securityContext);
MockHttpServletRequest request = createClientCredentialsTokenRequest(registeredClient);
public void doFilterWhenClientCredentialsTokenRequestMultipleScopeThenInvalidRequestError() throws Exception {
MockHttpServletRequest request = createClientCredentialsTokenRequest(
TestRegisteredClients.registeredClient2().build());
request.addParameter(OAuth2ParameterNames.SCOPE, "profile");
doFilterWhenTokenRequestInvalidParameterThenError(
@@ -311,6 +307,89 @@ public class OAuth2TokenEndpointFilterTests {
assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());
}
@Test
public void doFilterWhenRefreshTokenRequestMissingRefreshTokenThenInvalidRequestError() throws Exception {
MockHttpServletRequest request = createRefreshTokenTokenRequest(
TestRegisteredClients.registeredClient().build());
request.removeParameter(OAuth2ParameterNames.REFRESH_TOKEN);
doFilterWhenTokenRequestInvalidParameterThenError(
OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request);
}
@Test
public void doFilterWhenRefreshTokenRequestMultipleRefreshTokenThenInvalidRequestError() throws Exception {
MockHttpServletRequest request = createRefreshTokenTokenRequest(
TestRegisteredClients.registeredClient().build());
request.addParameter(OAuth2ParameterNames.REFRESH_TOKEN, "refresh-token-2");
doFilterWhenTokenRequestInvalidParameterThenError(
OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request);
}
@Test
public void doFilterWhenRefreshTokenRequestMultipleScopeThenInvalidRequestError() throws Exception {
MockHttpServletRequest request = createRefreshTokenTokenRequest(
TestRegisteredClients.registeredClient().build());
request.addParameter(OAuth2ParameterNames.SCOPE, "profile");
doFilterWhenTokenRequestInvalidParameterThenError(
OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, request);
}
@Test
public void doFilterWhenRefreshTokenRequestValidThenAccessTokenResponse() throws Exception {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "token",
Instant.now(), Instant.now().plus(Duration.ofHours(1)),
new HashSet<>(Arrays.asList("scope1", "scope2")));
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now());
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
new OAuth2AccessTokenAuthenticationToken(
registeredClient, clientPrincipal, accessToken, refreshToken);
when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(clientPrincipal);
SecurityContextHolder.setContext(securityContext);
MockHttpServletRequest request = createRefreshTokenTokenRequest(registeredClient);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verifyNoInteractions(filterChain);
ArgumentCaptor<OAuth2RefreshTokenAuthenticationToken> refreshTokenAuthenticationCaptor =
ArgumentCaptor.forClass(OAuth2RefreshTokenAuthenticationToken.class);
verify(this.authenticationManager).authenticate(refreshTokenAuthenticationCaptor.capture());
OAuth2RefreshTokenAuthenticationToken refreshTokenAuthenticationToken =
refreshTokenAuthenticationCaptor.getValue();
assertThat(refreshTokenAuthenticationToken.getRefreshToken()).isEqualTo(refreshToken.getTokenValue());
assertThat(refreshTokenAuthenticationToken.getPrincipal()).isEqualTo(clientPrincipal);
assertThat(refreshTokenAuthenticationToken.getScopes()).isEqualTo(registeredClient.getScopes());
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response);
OAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken();
assertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType());
assertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue());
assertThat(accessTokenResult.getIssuedAt()).isBetween(
accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1));
assertThat(accessTokenResult.getExpiresAt()).isBetween(
accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1));
assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes());
OAuth2RefreshToken refreshTokenResult = accessTokenResponse.getRefreshToken();
assertThat(refreshTokenResult.getTokenValue()).isEqualTo(refreshToken.getTokenValue());
}
private void doFilterWhenTokenRequestInvalidParameterThenError(String parameterName, String errorCode,
MockHttpServletRequest request) throws Exception {
@@ -366,4 +445,17 @@ public class OAuth2TokenEndpointFilterTests {
return request;
}
private static MockHttpServletRequest createRefreshTokenTokenRequest(RegisteredClient registeredClient) {
String requestUri = OAuth2TokenEndpointFilter.DEFAULT_TOKEN_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
request.setServletPath(requestUri);
request.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue());
request.addParameter(OAuth2ParameterNames.REFRESH_TOKEN, "refresh-token");
request.addParameter(OAuth2ParameterNames.SCOPE,
StringUtils.collectionToDelimitedString(registeredClient.getScopes(), " "));
return request;
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.server.authorization.web;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.mock.http.client.MockClientHttpResponse;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames2;
import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter;
import org.springframework.security.oauth2.server.authorization.TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashSet;
import java.util.function.Consumer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
/**
* Tests for {@link OAuth2TokenRevocationEndpointFilter}.
*
* @author Vivek Babu
* @author Joe Grandja
*/
public class OAuth2TokenRevocationEndpointFilterTests {
private AuthenticationManager authenticationManager;
private OAuth2TokenRevocationEndpointFilter filter;
private final HttpMessageConverter<OAuth2Error> errorHttpResponseConverter =
new OAuth2ErrorHttpMessageConverter();
@Before
public void setUp() {
this.authenticationManager = mock(AuthenticationManager.class);
this.filter = new OAuth2TokenRevocationEndpointFilter(this.authenticationManager);
}
@After
public void cleanup() {
SecurityContextHolder.clearContext();
}
@Test
public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationEndpointFilter(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("authenticationManager cannot be null");
}
@Test
public void constructorWhenTokenRevocationEndpointUriNullThenThrowIllegalArgumentException() {
assertThatThrownBy(() -> new OAuth2TokenRevocationEndpointFilter(this.authenticationManager, null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("tokenRevocationEndpointUri cannot be empty");
}
@Test
public void doFilterWhenNotTokenRevocationRequestThenNotProcessed() throws Exception {
String requestUri = "/path";
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
request.setServletPath(requestUri);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Test
public void doFilterWhenTokenRevocationRequestGetThenNotProcessed() throws Exception {
String requestUri = OAuth2TokenRevocationEndpointFilter.DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
request.setServletPath(requestUri);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Test
public void doFilterWhenTokenRevocationRequestMissingTokenThenInvalidRequestError() throws Exception {
doFilterWhenTokenRevocationRequestInvalidParameterThenError(
OAuth2ParameterNames2.TOKEN,
OAuth2ErrorCodes.INVALID_REQUEST,
request -> request.removeParameter(OAuth2ParameterNames2.TOKEN));
}
@Test
public void doFilterWhenTokenRevocationRequestMultipleTokenThenInvalidRequestError() throws Exception {
doFilterWhenTokenRevocationRequestInvalidParameterThenError(
OAuth2ParameterNames2.TOKEN,
OAuth2ErrorCodes.INVALID_REQUEST,
request -> request.addParameter(OAuth2ParameterNames2.TOKEN, "token-2"));
}
@Test
public void doFilterWhenTokenRevocationRequestMultipleTokenTypeHintThenInvalidRequestError() throws Exception {
doFilterWhenTokenRevocationRequestInvalidParameterThenError(
OAuth2ParameterNames2.TOKEN_TYPE_HINT,
OAuth2ErrorCodes.INVALID_REQUEST,
request -> request.addParameter(OAuth2ParameterNames2.TOKEN_TYPE_HINT, TokenType.ACCESS_TOKEN.getValue()));
}
@Test
public void doFilterWhenTokenRevocationRequestValidThenSuccessResponse() throws Exception {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient);
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, "token",
Instant.now(), Instant.now().plus(Duration.ofHours(1)),
new HashSet<>(Arrays.asList("scope1", "scope2")));
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication =
new OAuth2TokenRevocationAuthenticationToken(
accessToken, clientPrincipal);
when(this.authenticationManager.authenticate(any())).thenReturn(tokenRevocationAuthentication);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(clientPrincipal);
SecurityContextHolder.setContext(securityContext);
MockHttpServletRequest request = createTokenRevocationRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verifyNoInteractions(filterChain);
verify(this.authenticationManager).authenticate(any());
assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
}
private void doFilterWhenTokenRevocationRequestInvalidParameterThenError(String parameterName, String errorCode,
Consumer<MockHttpServletRequest> requestConsumer) throws Exception {
MockHttpServletRequest request = createTokenRevocationRequest();
requestConsumer.accept(request);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
this.filter.doFilter(request, response, filterChain);
verifyNoInteractions(filterChain);
assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
OAuth2Error error = readError(response);
assertThat(error.getErrorCode()).isEqualTo(errorCode);
assertThat(error.getDescription()).isEqualTo("OAuth 2.0 Token Revocation Parameter: " + parameterName);
}
private OAuth2Error readError(MockHttpServletResponse response) throws Exception {
MockClientHttpResponse httpResponse = new MockClientHttpResponse(
response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus()));
return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse);
}
private static MockHttpServletRequest createTokenRevocationRequest() {
String requestUri = OAuth2TokenRevocationEndpointFilter.DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI;
MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri);
request.setServletPath(requestUri);
request.addParameter(OAuth2ParameterNames2.TOKEN, "token");
request.addParameter(OAuth2ParameterNames2.TOKEN_TYPE_HINT, TokenType.ACCESS_TOKEN.getValue());
return request;
}
}

View File

@@ -45,11 +45,14 @@ public class PublicClientAuthenticationConverterTests {
}
@Test
public void convertWhenMissingClientIdThenReturnNull() {
public void convertWhenMissingClientIdThenInvalidRequestError() {
MockHttpServletRequest request = createPkceTokenRequest();
request.removeParameter(OAuth2ParameterNames.CLIENT_ID);
Authentication authentication = this.converter.convert(request);
assertThat(authentication).isNull();
assertThatThrownBy(() -> this.converter.convert(request))
.isInstanceOf(OAuth2AuthenticationException.class)
.extracting(ex -> ((OAuth2AuthenticationException) ex).getError())
.extracting("errorCode")
.isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST);
}
@Test

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69
@@ -45,12 +45,12 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.attoparser:attoparser:2.0.5.RELEASE
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.reactivestreams:reactive-streams:1.0.3

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69
@@ -45,12 +45,12 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.attoparser:attoparser:2.0.5.RELEASE
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.reactivestreams:reactive-streams:1.0.3

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69
@@ -45,12 +45,12 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.attoparser:attoparser:2.0.5.RELEASE
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.reactivestreams:reactive-streams:1.0.3

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69
@@ -45,12 +45,12 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.attoparser:attoparser:2.0.5.RELEASE
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.reactivestreams:reactive-streams:1.0.3

View File

@@ -12,8 +12,8 @@ com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:content-type:2.1
com.nimbusds:lang-tag:1.4.4
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:oauth2-oidc-sdk:8.22
com.nimbusds:nimbus-jose-jwt:9.1.2
com.nimbusds:oauth2-oidc-sdk:8.25
com.sun.activation:jakarta.activation:1.2.2
com.sun.mail:jakarta.mail:1.6.5
io.github.classgraph:classgraph:4.8.69
@@ -45,12 +45,12 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.attoparser:attoparser:2.0.5.RELEASE
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.ow2.asm:asm:5.0.4
org.reactivestreams:reactive-streams:1.0.3

View File

@@ -50,6 +50,7 @@ public class WebClientConfig {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30

View File

@@ -10,7 +10,7 @@ com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.2
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.2
com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.2
com.github.stephenc.jcip:jcip-annotations:1.0-1
com.nimbusds:nimbus-jose-jwt:9.0.1
com.nimbusds:nimbus-jose-jwt:9.1.2
jakarta.annotation:jakarta.annotation-api:1.3.5
junit:junit:4.13.1
net.bytebuddy:byte-buddy-agent:1.10.14
@@ -19,11 +19,11 @@ org.apache.logging.log4j:log4j-api:2.13.3
org.apache.logging.log4j:log4j-to-slf4j:2.13.3
org.apache.tomcat.embed:tomcat-embed-core:9.0.38
org.apache.tomcat.embed:tomcat-embed-websocket:9.0.38
org.assertj:assertj-core:3.17.2
org.assertj:assertj-core:3.18.0
org.glassfish:jakarta.el:3.0.3
org.hamcrest:hamcrest-core:2.2
org.hamcrest:hamcrest:2.2
org.mockito:mockito-core:3.5.13
org.mockito:mockito-core:3.6.0
org.objenesis:objenesis:3.1
org.slf4j:jul-to-slf4j:1.7.30
org.slf4j:slf4j-api:1.7.30