Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db4dd3f08e | ||
|
|
6e2f2fe8a4 | ||
|
|
061d9f8c18 | ||
|
|
59f77d034e | ||
|
|
bf24cfb19e | ||
|
|
77a9b2ebf3 | ||
|
|
d76d209124 | ||
|
|
58ad2d2c6c | ||
|
|
1a6b3e3e59 | ||
|
|
e61639bf7f | ||
|
|
7e2264204b | ||
|
|
43b44a1f77 | ||
|
|
19d6e97372 | ||
|
|
cff7b786de | ||
|
|
edf23562cb | ||
|
|
e7909d0cdd | ||
|
|
e49d4a79b4 | ||
|
|
7720e275e4 | ||
|
|
6a2c841d06 | ||
|
|
d7fe79d0ec | ||
|
|
40ca7a4654 | ||
|
|
06bf391bfa | ||
|
|
bfb5432b46 | ||
|
|
9818618ea3 | ||
|
|
cb09aef605 | ||
|
|
ebcdf7989d | ||
|
|
df8793c902 | ||
|
|
6c7486429c | ||
|
|
cf82c06502 | ||
|
|
a2167a5091 | ||
|
|
78d4bd0bad | ||
|
|
1ce77d3caa | ||
|
|
b7ddb837d6 | ||
|
|
dc94e5e161 | ||
|
|
18f8b3afaa | ||
|
|
601640e4fa | ||
|
|
af60f3d4d0 | ||
|
|
7b1b965c08 | ||
|
|
0c6b1251ce |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
authenticatePkceIfAvailable(clientAuthentication, registeredClient);
|
||||
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)) {
|
||||
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()) {
|
||||
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();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean authorizationCodeGrant(Map<String, Object> parameters) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(); }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -50,6 +50,7 @@ public class WebClientConfig {
|
||||
OAuth2AuthorizedClientProvider authorizedClientProvider =
|
||||
OAuth2AuthorizedClientProviderBuilder.builder()
|
||||
.authorizationCode()
|
||||
.refreshToken()
|
||||
.clientCredentials()
|
||||
.build();
|
||||
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user