├── docs ├── modules │ └── ROOT │ │ ├── examples │ │ ├── docs-src │ │ └── samples │ │ ├── pages │ │ ├── how-to.adoc │ │ ├── index.adoc │ │ └── getting-help.adoc │ │ └── nav.adoc ├── src │ ├── main │ │ ├── resources │ │ │ └── application.yml │ │ └── java │ │ │ └── sample │ │ │ ├── pkce │ │ │ ├── application.yml │ │ │ └── ClientConfig.java │ │ │ ├── sociallogin │ │ │ └── application.yml │ │ │ ├── gettingstarted │ │ │ └── application.yml │ │ │ ├── jpa │ │ │ └── repository │ │ │ │ ├── client │ │ │ │ └── ClientRepository.java │ │ │ │ ├── authorizationconsent │ │ │ │ └── AuthorizationConsentRepository.java │ │ │ │ └── authorization │ │ │ │ └── AuthorizationRepository.java │ │ │ ├── customclaims │ │ │ ├── CustomClaimsConfiguration.java │ │ │ └── authorities │ │ │ │ └── CustomClaimsWithAuthoritiesConfiguration.java │ │ │ ├── userinfo │ │ │ ├── jwt │ │ │ │ └── JwtTokenCustomizerConfig.java │ │ │ └── idtoken │ │ │ │ └── IdTokenCustomizerConfig.java │ │ │ ├── extgrant │ │ │ └── CustomCodeGrantAuthenticationToken.java │ │ │ └── registration │ │ │ ├── ClientConfig.java │ │ │ └── SecurityConfig.java │ └── test │ │ └── java │ │ └── sample │ │ ├── util │ │ └── RegisteredClients.java │ │ └── test │ │ └── SpringTestContextExtension.java ├── antora.yml └── spring-authorization-server-docs.gradle ├── samples ├── demo-client │ ├── gradle.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── keystore.p12 │ │ │ ├── keystore-self-signed.p12 │ │ │ ├── static │ │ │ │ └── assets │ │ │ │ │ └── img │ │ │ │ │ ├── devices.png │ │ │ │ │ └── spring-security.svg │ │ │ └── templates │ │ │ │ ├── index.html │ │ │ │ ├── logged-out.html │ │ │ │ └── device-activate.html │ │ │ └── java │ │ │ └── sample │ │ │ ├── DemoClientApplication.java │ │ │ ├── web │ │ │ ├── DefaultController.java │ │ │ └── JwkSetController.java │ │ │ └── authorization │ │ │ └── OAuth2DeviceGrantRequest.java │ └── samples-demo-client.gradle ├── users-resource │ ├── gradle.properties │ ├── samples-users-resource.gradle │ └── src │ │ └── main │ │ ├── resources │ │ └── application.yml │ │ └── java │ │ └── sample │ │ ├── UsersResourceApplication.java │ │ ├── config │ │ └── SecurityConfig.java │ │ └── web │ │ └── UserController.java ├── messages-resource │ ├── gradle.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── keystore.p12 │ │ │ └── application.yml │ │ │ └── java │ │ │ └── sample │ │ │ ├── web │ │ │ └── MessagesController.java │ │ │ ├── MessagesResourceApplication.java │ │ │ └── config │ │ │ ├── TomcatServerConfig.java │ │ │ └── ResourceServerConfig.java │ └── samples-messages-resource.gradle ├── demo-authorizationserver │ ├── gradle.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ ├── keystore.p12 │ │ │ ├── static │ │ │ │ └── assets │ │ │ │ │ ├── img │ │ │ │ │ ├── devices.png │ │ │ │ │ ├── github.png │ │ │ │ │ └── google.png │ │ │ │ │ └── css │ │ │ │ │ └── signin.css │ │ │ ├── templates │ │ │ │ ├── error.html │ │ │ │ ├── device-activated.html │ │ │ │ ├── device-activate.html │ │ │ │ └── login.html │ │ │ └── application.yml │ │ │ └── java │ │ │ └── sample │ │ │ ├── web │ │ │ ├── LoginController.java │ │ │ ├── DeviceController.java │ │ │ └── DefaultErrorController.java │ │ │ ├── config │ │ │ └── TomcatServerConfig.java │ │ │ ├── authentication │ │ │ └── DeviceClientAuthenticationToken.java │ │ │ ├── federation │ │ │ └── UserRepositoryOAuth2UserHandler.java │ │ │ ├── DemoAuthorizationServerApplication.java │ │ │ └── jose │ │ │ └── Jwks.java │ └── samples-demo-authorizationserver.gradle ├── default-authorizationserver │ ├── gradle.properties │ ├── samples-default-authorizationserver.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── sample │ │ │ └── DefaultAuthorizationServerApplication.java │ │ └── resources │ │ └── application.yml └── x509-certificate-generator │ ├── src │ └── main │ │ └── resources │ │ └── application.yml │ ├── generated │ ├── spring-samples-ca-keystore.p12 │ ├── spring-samples-trusted-ca-keystore.p12 │ ├── spring-samples-trusted-ca.pem │ └── spring-samples-ca.pem │ └── samples-x509-certificate-generator.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── scripts ├── update-dependencies.sh └── release │ ├── wait-for-done.sh │ └── release-notes-sections.yml ├── oauth2-authorization-server ├── src │ ├── main │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ └── spring │ │ │ │ │ └── aot.factories │ │ │ └── org │ │ │ │ └── springframework │ │ │ │ └── security │ │ │ │ └── oauth2 │ │ │ │ └── server │ │ │ │ └── authorization │ │ │ │ ├── oauth2-authorization-consent-schema.sql │ │ │ │ ├── client │ │ │ │ └── oauth2-registered-client-schema.sql │ │ │ │ └── oauth2-authorization-schema.sql │ │ └── java │ │ │ └── org │ │ │ └── springframework │ │ │ └── security │ │ │ └── oauth2 │ │ │ └── server │ │ │ └── authorization │ │ │ ├── jackson2 │ │ │ ├── HashSetMixin.java │ │ │ ├── JwsAlgorithmMixin.java │ │ │ ├── UnmodifiableMapMixin.java │ │ │ ├── OAuth2TokenFormatMixin.java │ │ │ ├── OAuth2AuthorizationRequestMixin.java │ │ │ ├── DurationMixin.java │ │ │ ├── OAuth2TokenExchangeActorMixin.java │ │ │ ├── UnmodifiableMapDeserializer.java │ │ │ ├── OAuth2TokenExchangeCompositeAuthenticationTokenMixin.java │ │ │ └── JsonNodeUtils.java │ │ │ ├── util │ │ │ └── SpringAuthorizationServerVersion.java │ │ │ ├── token │ │ │ ├── OAuth2TokenCustomizer.java │ │ │ ├── OAuth2TokenGenerator.java │ │ │ ├── DefaultOAuth2TokenContext.java │ │ │ └── OAuth2TokenClaimNames.java │ │ │ ├── OAuth2AuthorizationCode.java │ │ │ ├── config │ │ │ └── annotation │ │ │ │ └── web │ │ │ │ └── configurers │ │ │ │ └── AbstractOAuth2Configurer.java │ │ │ ├── client │ │ │ └── RegisteredClientRepository.java │ │ │ ├── context │ │ │ ├── AuthorizationServerContextHolder.java │ │ │ ├── Context.java │ │ │ └── AuthorizationServerContext.java │ │ │ ├── OAuth2AuthorizationConsentService.java │ │ │ ├── OAuth2AuthorizationService.java │ │ │ ├── authentication │ │ │ ├── OAuth2DeviceCodeAuthenticationToken.java │ │ │ ├── OAuth2TokenExchangeActor.java │ │ │ └── OAuth2ClientCredentialsAuthenticationToken.java │ │ │ ├── http │ │ │ └── converter │ │ │ │ └── HttpMessageConverters.java │ │ │ ├── oidc │ │ │ ├── http │ │ │ │ └── converter │ │ │ │ │ └── HttpMessageConverters.java │ │ │ ├── web │ │ │ │ └── authentication │ │ │ │ │ └── OAuth2EndpointUtils.java │ │ │ └── OidcProviderMetadataClaimNames.java │ │ │ └── OAuth2TokenType.java │ └── test │ │ ├── resources │ │ └── org │ │ │ └── springframework │ │ │ └── security │ │ │ └── oauth2 │ │ │ └── server │ │ │ └── authorization │ │ │ ├── custom-oauth2-authorization-consent-schema.sql │ │ │ ├── client │ │ │ └── custom-oauth2-registered-client-schema.sql │ │ │ ├── custom-oauth2-authorization-schema.sql │ │ │ └── custom-oauth2-authorization-schema-clob-data-type.sql │ │ └── java │ │ └── org │ │ └── springframework │ │ └── security │ │ └── oauth2 │ │ ├── jwt │ │ ├── TestJwtClaimsSets.java │ │ └── TestJwsHeaders.java │ │ └── server │ │ └── authorization │ │ ├── authentication │ │ └── OAuth2TokenExchangeActorTests.java │ │ ├── context │ │ └── TestAuthorizationServerContext.java │ │ ├── config │ │ └── annotation │ │ │ └── web │ │ │ └── configuration │ │ │ └── OAuth2AuthorizationServerConfigurationTests.java │ │ ├── jackson2 │ │ └── TestingAuthenticationTokenMixin.java │ │ └── test │ │ └── SpringTestContextExtension.java └── spring-security-oauth2-authorization-server.gradle ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── update-scheduled-release-version.yml │ ├── pr-build-workflow.yml │ ├── release-scheduler.yml │ ├── deploy-docs.yml │ └── continuous-integration-workflow.yml ├── etc ├── nohttp │ ├── suppressions.xml │ ├── allowlist.lines │ └── checkstyle.xml └── checkstyle │ ├── header.txt │ ├── suppressions.xml │ └── checkstyle.xml ├── .editorconfig ├── gradle.properties ├── .gitignore ├── dependencies └── spring-authorization-server-dependencies.gradle ├── settings.gradle ├── git └── hooks │ └── prepare-forward-merge └── CODE_OF_CONDUCT.adoc /docs/modules/ROOT/examples/docs-src: -------------------------------------------------------------------------------- 1 | ../../../src -------------------------------------------------------------------------------- /docs/modules/ROOT/examples/samples: -------------------------------------------------------------------------------- 1 | ../../../../samples -------------------------------------------------------------------------------- /samples/demo-client/gradle.properties: -------------------------------------------------------------------------------- 1 | spring-security.version=6.3.0-RC1 2 | -------------------------------------------------------------------------------- /samples/users-resource/gradle.properties: -------------------------------------------------------------------------------- 1 | spring-security.version=6.3.0-RC1 2 | -------------------------------------------------------------------------------- /samples/messages-resource/gradle.properties: -------------------------------------------------------------------------------- 1 | spring-security.version=6.3.0-RC1 2 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/gradle.properties: -------------------------------------------------------------------------------- 1 | spring-security.version=6.3.0-RC1 2 | -------------------------------------------------------------------------------- /samples/default-authorizationserver/gradle.properties: -------------------------------------------------------------------------------- 1 | spring-security.version=6.3.0-RC1 2 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/how-to.adoc: -------------------------------------------------------------------------------- 1 | 2 | [[how-to]] 3 | = How-to Guides 4 | :page-section-summary-toc: 1 5 | -------------------------------------------------------------------------------- /samples/x509-certificate-generator/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | main: 3 | web-application-type: none 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /docs/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | 4 | logging: 5 | level: 6 | org.springframework.security: trace 7 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-client/src/main/resources/keystore.p12 -------------------------------------------------------------------------------- /samples/messages-resource/src/main/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/messages-resource/src/main/resources/keystore.p12 -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-authorizationserver/src/main/resources/keystore.p12 -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/keystore-self-signed.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-client/src/main/resources/keystore-self-signed.p12 -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/static/assets/img/devices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-client/src/main/resources/static/assets/img/devices.png -------------------------------------------------------------------------------- /samples/x509-certificate-generator/generated/spring-samples-ca-keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/x509-certificate-generator/generated/spring-samples-ca-keystore.p12 -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/static/assets/img/devices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-authorizationserver/src/main/resources/static/assets/img/devices.png -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/static/assets/img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-authorizationserver/src/main/resources/static/assets/img/github.png -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/static/assets/img/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/demo-authorizationserver/src/main/resources/static/assets/img/google.png -------------------------------------------------------------------------------- /samples/x509-certificate-generator/generated/spring-samples-trusted-ca-keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjohnr/spring-authorization-server/HEAD/samples/x509-certificate-generator/generated/spring-samples-trusted-ca-keystore.p12 -------------------------------------------------------------------------------- /scripts/update-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f build/updates.txt 3 | ./gradlew dependencyUpdate -Drevision=release 4 | find . -name report.txt | xargs cat > build/updates.txt 5 | echo "Updates...." 6 | cat build/updates.txt | fgrep ' ->' | sort | uniq 7 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/resources/META-INF/spring/aot.factories: -------------------------------------------------------------------------------- 1 | org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ 2 | org.springframework.security.oauth2.server.authorization.aot.hint.OAuth2AuthorizationServerBeanRegistrationAotProcessor 3 | -------------------------------------------------------------------------------- /scripts/release/wait-for-done.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=$1 4 | until http -h --check-status --ignore-stdin https://repo1.maven.org/maven2/org/springframework/security/spring-security-oauth2-authorization-server/$VERSION/; do sleep 10; clear; done; spd-say "It is now uploaded" 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Community Support 4 | url: https://stackoverflow.com/questions/tagged/spring-authorization-server 5 | about: Please ask and answer questions on StackOverflow with the tag `spring-authorization-server`. 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /etc/nohttp/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /etc/nohttp/allowlist.lines: -------------------------------------------------------------------------------- 1 | ^http://[^/]*nabble.com.* 2 | ^http://blog.opensecurityresearch.com/.* 3 | ^http://iharder.sourceforge.net/current/java/base64/ 4 | ^http://jaspan.com.* 5 | ^http://lists.webappsec.org/.* 6 | ^http://webblaze.cs.berkeley.edu/.* 7 | ^http://www.w3.org/2000/09/xmldsig.* 8 | ^http://www.gnu.org/.* 9 | ^http://www.gradle.org -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-consent-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE oauth2AuthorizationConsent ( 2 | registeredClientId varchar(100) NOT NULL, 3 | principalName varchar(200) NOT NULL, 4 | authorities varchar(1000) NOT NULL, 5 | PRIMARY KEY (registeredClientId, principalName) 6 | ); 7 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE oauth2_authorization_consent ( 2 | registered_client_id varchar(100) NOT NULL, 3 | principal_name varchar(200) NOT NULL, 4 | authorities varchar(1000) NOT NULL, 5 | PRIMARY KEY (registered_client_id, principal_name) 6 | ); 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 120 8 | 9 | [*.{java,xml}] 10 | indent_style = tab 11 | indent_size = 4 12 | charset = utf-8 13 | continuation_indent_size = 8 14 | 15 | ij_smart_tabs = false 16 | ij_java_align_multiline_parameters = false 17 | 18 | [*.gradle] 19 | indent_style = tab 20 | -------------------------------------------------------------------------------- /.github/workflows/update-scheduled-release-version.yml: -------------------------------------------------------------------------------- 1 | name: Update Scheduled Release Version 2 | 3 | on: 4 | workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main. 5 | 6 | jobs: 7 | update-scheduled-release-version: 8 | name: Update Scheduled Release Version 9 | uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@v1 10 | secrets: inherit 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.3.0-SNAPSHOT 2 | org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError 3 | org.gradle.parallel=true 4 | org.gradle.caching=true 5 | springFrameworkVersion=6.1.6 6 | springSecurityVersion=6.3.0-RC1 7 | springJavaformatVersion=0.0.38 8 | springJavaformatExcludePackages=org/springframework/security/config org/springframework/security/oauth2 9 | checkstyleToolVersion=8.34 10 | nohttpCheckstyleVersion=0.0.11 11 | jacocoToolVersion=0.8.7 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'type: enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Expected Behavior** 11 | 12 | 13 | **Current Behavior** 14 | 15 | 16 | **Context** 17 | 20 | -------------------------------------------------------------------------------- /docs/modules/ROOT/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:overview.adoc[] 2 | * xref:getting-help.adoc[] 3 | * xref:getting-started.adoc[] 4 | * xref:configuration-model.adoc[] 5 | * xref:core-model-components.adoc[] 6 | * xref:protocol-endpoints.adoc[] 7 | * xref:how-to.adoc[] 8 | ** xref:guides/how-to-pkce.adoc[] 9 | ** xref:guides/how-to-social-login.adoc[] 10 | ** xref:guides/how-to-ext-grant-type.adoc[] 11 | ** xref:guides/how-to-userinfo.adoc[] 12 | ** xref:guides/how-to-jpa.adoc[] 13 | ** xref:guides/how-to-custom-claims-authorities.adoc[] 14 | ** xref:guides/how-to-dynamic-client-registration.adoc[] 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | bin/ 7 | classes/ 8 | target/ 9 | *.log 10 | *.log.* 11 | 12 | ### STS ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### IntelliJ IDEA ### 22 | .idea 23 | *.iws 24 | *.iml 25 | *.ipr 26 | out/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ### Mac ### 39 | .DS_Store 40 | cached-antora-playbook.yml 41 | -------------------------------------------------------------------------------- /samples/x509-certificate-generator/samples-x509-certificate-generator.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.springframework.boot" version "3.2.2" 3 | id "io.spring.dependency-management" version "1.1.0" 4 | id "java" 5 | } 6 | 7 | group = project.rootProject.group 8 | version = project.rootProject.version 9 | 10 | java { 11 | sourceCompatibility = JavaVersion.VERSION_17 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | } 17 | 18 | dependencies { 19 | implementation "org.springframework.boot:spring-boot-starter" 20 | implementation "org.bouncycastle:bcpkix-jdk18on:1.77" 21 | implementation "org.bouncycastle:bcprov-jdk18on:1.77" 22 | } 23 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/pkce/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | security: 3 | oauth2: 4 | authorizationserver: 5 | client: 6 | public-client: 7 | registration: 8 | client-id: "public-client" 9 | client-authentication-methods: 10 | - "none" 11 | authorization-grant-types: 12 | - "authorization_code" 13 | redirect-uris: 14 | - "http://127.0.0.1:4200" 15 | scopes: 16 | - "openid" 17 | - "profile" 18 | require-authorization-consent: true 19 | require-proof-key: true 20 | -------------------------------------------------------------------------------- /.github/workflows/pr-build-workflow.yml: -------------------------------------------------------------------------------- 1 | name: PR build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '**' 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest] 15 | jdk: [17] 16 | fail-fast: false 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Set up JDK ${{ matrix.jdk }} 20 | uses: spring-io/spring-gradle-build-action@v2 21 | with: 22 | java-version: ${{ matrix.jdk }} 23 | distribution: 'temurin' 24 | - name: Build with Gradle 25 | run: ./gradlew clean build 26 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/static/assets/img/spring-security.svg: -------------------------------------------------------------------------------- 1 | logo-security -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/static/assets/css/signin.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | } 5 | 6 | body { 7 | display: flex; 8 | align-items: start; 9 | padding-top: 100px; 10 | background-color: #f5f5f5; 11 | } 12 | 13 | .form-signin { 14 | max-width: 330px; 15 | padding: 15px; 16 | } 17 | 18 | .form-signin .form-floating:focus-within { 19 | z-index: 2; 20 | } 21 | 22 | .form-signin input[type="username"] { 23 | margin-bottom: -1px; 24 | border-bottom-right-radius: 0; 25 | border-bottom-left-radius: 0; 26 | } 27 | 28 | .form-signin input[type="password"] { 29 | margin-bottom: 10px; 30 | border-top-left-radius: 0; 31 | border-top-right-radius: 0; 32 | } 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'type: bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Sample** 20 | 21 | A link to a GitHub repository with a [minimal, reproducible sample](https://stackoverflow.com/help/minimal-reproducible-example). 22 | 23 | Reports that include a sample will take priority over reports that do not. 24 | At times, we may require a sample, so it is good to try and include a sample up front. 25 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/sociallogin/application.yml: -------------------------------------------------------------------------------- 1 | okta: 2 | base-url: ${OKTA_BASE_URL} 3 | 4 | spring: 5 | security: 6 | oauth2: 7 | client: 8 | registration: 9 | my-client: 10 | provider: okta 11 | client-id: ${OKTA_CLIENT_ID} 12 | client-secret: ${OKTA_CLIENT_SECRET} 13 | scope: 14 | - openid 15 | - profile 16 | - email 17 | provider: 18 | okta: 19 | authorization-uri: ${okta.base-url}/oauth2/v1/authorize 20 | token-uri: ${okta.base-url}/oauth2/v1/token 21 | user-info-uri: ${okta.base-url}/oauth2/v1/userinfo 22 | jwk-set-uri: ${okta.base-url}/oauth2/v1/keys 23 | user-name-attribute: sub -------------------------------------------------------------------------------- /samples/messages-resource/samples-messages-resource.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.springframework.boot" version "3.2.2" 3 | id "io.spring.dependency-management" version "1.1.0" 4 | id "java" 5 | } 6 | 7 | group = project.rootProject.group 8 | version = project.rootProject.version 9 | 10 | java { 11 | sourceCompatibility = JavaVersion.VERSION_17 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | maven { url "https://repo.spring.io/milestone" } 17 | maven { url "https://repo.spring.io/snapshot" } 18 | } 19 | 20 | dependencies { 21 | implementation "org.springframework.boot:spring-boot-starter-web" 22 | implementation "org.springframework.boot:spring-boot-starter-security" 23 | implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server" 24 | } 25 | -------------------------------------------------------------------------------- /samples/users-resource/samples-users-resource.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.springframework.boot" version "3.2.2" 3 | id "io.spring.dependency-management" version "1.1.0" 4 | id "java" 5 | } 6 | 7 | group = project.rootProject.group 8 | version = project.rootProject.version 9 | sourceCompatibility = "17" 10 | 11 | repositories { 12 | mavenCentral() 13 | maven { url "https://repo.spring.io/milestone" } 14 | maven { url "https://repo.spring.io/snapshot" } 15 | } 16 | 17 | dependencies { 18 | implementation "org.springframework.boot:spring-boot-starter-web" 19 | implementation "org.springframework.boot:spring-boot-starter-security" 20 | implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server" 21 | implementation "org.springframework.boot:spring-boot-starter-oauth2-client" 22 | } 23 | -------------------------------------------------------------------------------- /etc/nohttp/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /etc/checkstyle/header.txt: -------------------------------------------------------------------------------- 1 | ^\Q/*\E$ 2 | ^\Q * Copyright\E (\d{4}(\-\d{4})? the original author or authors\.|(\d{4}, )*(\d{4}) Acegi Technology Pty Limited)$ 3 | ^\Q *\E$ 4 | ^\Q * Licensed under the Apache License, Version 2.0 (the "License");\E$ 5 | ^\Q * you may not use this file except in compliance with the License.\E$ 6 | ^\Q * You may obtain a copy of the License at\E$ 7 | ^\Q *\E$ 8 | ^\Q * https://www.apache.org/licenses/LICENSE-2.0\E$ 9 | ^\Q *\E$ 10 | ^\Q * Unless required by applicable law or agreed to in writing, software\E$ 11 | ^\Q * distributed under the License is distributed on an "AS IS" BASIS,\E$ 12 | ^\Q * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\E$ 13 | ^\Q * See the License for the specific language governing permissions and\E$ 14 | ^\Q * limitations under the License.\E$ 15 | ^\Q */\E$ 16 | ^.*$ 17 | -------------------------------------------------------------------------------- /.github/workflows/release-scheduler.yml: -------------------------------------------------------------------------------- 1 | name: Release Scheduler 2 | 3 | on: 4 | schedule: 5 | - cron: '15 15 * * TUE' # Every Tuesday at 3:15pm UTC 6 | workflow_dispatch: 7 | 8 | permissions: read-all 9 | 10 | jobs: 11 | dispatch_scheduled_releases: 12 | name: Dispatch scheduled releases 13 | if: ${{ github.repository_owner == 'spring-projects' }} 14 | strategy: 15 | matrix: 16 | # List of active maintenance branches. 17 | branch: [ main, 1.2.x, 1.1.x ] 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 1 24 | - name: Dispatch 25 | env: 26 | GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }} 27 | run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }} 28 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/client/custom-oauth2-registered-client-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE oauth2RegisteredClient ( 2 | id varchar(100) NOT NULL, 3 | clientId varchar(100) NOT NULL, 4 | clientIdIssuedAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, 5 | clientSecret varchar(200) DEFAULT NULL, 6 | clientSecretExpiresAt timestamp DEFAULT NULL, 7 | clientName varchar(200) NOT NULL, 8 | clientAuthenticationMethods varchar(1000) NOT NULL, 9 | authorizationGrantTypes varchar(1000) NOT NULL, 10 | redirectUris varchar(1000) DEFAULT NULL, 11 | postLogoutRedirectUris varchar(1000) DEFAULT NULL, 12 | scopes varchar(1000) NOT NULL, 13 | clientSettings varchar(2000) NOT NULL, 14 | tokenSettings varchar(2000) NOT NULL, 15 | PRIMARY KEY (id) 16 | ); 17 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE oauth2_registered_client ( 2 | id varchar(100) NOT NULL, 3 | client_id varchar(100) NOT NULL, 4 | client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL, 5 | client_secret varchar(200) DEFAULT NULL, 6 | client_secret_expires_at timestamp DEFAULT NULL, 7 | client_name varchar(200) NOT NULL, 8 | client_authentication_methods varchar(1000) NOT NULL, 9 | authorization_grant_types varchar(1000) NOT NULL, 10 | redirect_uris varchar(1000) DEFAULT NULL, 11 | post_logout_redirect_uris varchar(1000) DEFAULT NULL, 12 | scopes varchar(1000) NOT NULL, 13 | client_settings varchar(2000) NOT NULL, 14 | token_settings varchar(2000) NOT NULL, 15 | PRIMARY KEY (id) 16 | ); 17 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |

14 |

15 |
16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /scripts/release/release-notes-sections.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | repository: spring-projects/spring-authorization-server 3 | sections: 4 | - title: ":star: New Features" 5 | labels: ["type: enhancement"] 6 | sort: "title" 7 | - title: ":beetle: Bug Fixes" 8 | labels: ["type: bug", "type: regression"] 9 | sort: "title" 10 | - title: ":hammer: Dependency Upgrades" 11 | labels: ["type: dependency-upgrade"] 12 | sort: "title" 13 | - title: ":rewind: Non-passive" 14 | labels: ["type: breaks-passivity"] 15 | sort: "title" 16 | issues: 17 | exclude: 18 | labels: [ "status: duplicate" ] 19 | ports: 20 | - label: "status: forward-port" 21 | bodyExpression: 'Forward port of issue #(\d+).*' 22 | contributors: 23 | title: ":heart: Contributors" 24 | exclude: 25 | names: ["jgrandja", "sjohnr"] 26 | -------------------------------------------------------------------------------- /docs/antora.yml: -------------------------------------------------------------------------------- 1 | name: authorization-server 2 | version: true 3 | title: Spring Authorization Server 4 | nav: 5 | - modules/ROOT/nav.adoc 6 | ext: 7 | collector: 8 | run: 9 | command: gradlew -q -PbuildSrc.skipTests=true "-Dorg.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError" :spring-authorization-server-docs:generateAntoraYml 10 | local: true 11 | scan: 12 | dir: ./build/generated-antora-resources 13 | 14 | asciidoc: 15 | attributes: 16 | attribute-missing: 'warn' 17 | chomp: 'all' 18 | spring-security-reference-base-url: "https://docs.spring.io/spring-security/reference" 19 | spring-security-api-base-url: "https://docs.spring.io/spring-security/site/docs/current/api" 20 | spring-boot-reference-base-url: "https://docs.spring.io/spring-boot/docs/current/reference/html" 21 | examples-dir: example$docs-src 22 | samples-dir: example$samples 23 | docs-java: '{examples-dir}/main/java' 24 | -------------------------------------------------------------------------------- /dependencies/spring-authorization-server-dependencies.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java-platform" 3 | } 4 | 5 | javaPlatform { 6 | allowDependencies() 7 | } 8 | 9 | dependencies { 10 | api platform("org.springframework:spring-framework-bom:$springFrameworkVersion") 11 | api platform("org.springframework.security:spring-security-bom:$springSecurityVersion") 12 | api platform("com.fasterxml.jackson:jackson-bom:2.17.0") 13 | constraints { 14 | api "com.nimbusds:nimbus-jose-jwt:9.37.3" 15 | api "jakarta.servlet:jakarta.servlet-api:6.0.0" 16 | api "org.bouncycastle:bcpkix-jdk18on:1.78" 17 | api "org.bouncycastle:bcprov-jdk18on:1.78" 18 | api "org.junit.jupiter:junit-jupiter:5.10.2" 19 | api "org.assertj:assertj-core:3.25.3" 20 | api "org.mockito:mockito-core:4.11.0" 21 | api "com.squareup.okhttp3:mockwebserver:4.12.0" 22 | api "com.squareup.okhttp3:okhttp:4.12.0" 23 | api "com.jayway.jsonpath:json-path:2.9.0" 24 | api "org.hsqldb:hsqldb:2.7.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/messages-resource/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8443 3 | ssl: 4 | bundle: messages-resource 5 | client-auth: need 6 | 7 | logging: 8 | level: 9 | root: INFO 10 | org.springframework.web: INFO 11 | org.springframework.security: INFO 12 | org.springframework.security.oauth2: INFO 13 | # org.springframework.boot.autoconfigure: DEBUG 14 | 15 | spring: 16 | ssl: 17 | bundle: 18 | jks: 19 | messages-resource: 20 | key: 21 | alias: messages-resource-sample 22 | password: password 23 | keystore: 24 | location: classpath:keystore.p12 25 | password: password 26 | type: PKCS12 27 | truststore: 28 | location: classpath:keystore.p12 29 | password: password 30 | type: PKCS12 31 | security: 32 | oauth2: 33 | resourceserver: 34 | jwt: 35 | jwk-set-uri: http://localhost:9000/oauth2/jwks 36 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/gettingstarted/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | 4 | logging: 5 | level: 6 | org.springframework.security: trace 7 | 8 | spring: 9 | security: 10 | user: 11 | name: user 12 | password: password 13 | oauth2: 14 | authorizationserver: 15 | client: 16 | oidc-client: 17 | registration: 18 | client-id: "oidc-client" 19 | client-secret: "{noop}secret" 20 | client-authentication-methods: 21 | - "client_secret_basic" 22 | authorization-grant-types: 23 | - "authorization_code" 24 | - "refresh_token" 25 | redirect-uris: 26 | - "http://127.0.0.1:8080/login/oauth2/code/oidc-client" 27 | post-logout-redirect-uris: 28 | - "http://127.0.0.1:8080/" 29 | scopes: 30 | - "openid" 31 | - "profile" 32 | require-authorization-consent: true 33 | -------------------------------------------------------------------------------- /etc/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Docs 2 | on: 3 | push: 4 | tags: '**' 5 | repository_dispatch: 6 | types: request-build-reference # legacy 7 | #schedule: 8 | #- cron: '0 10 * * *' # Once per day at 10am UTC 9 | workflow_dispatch: 10 | permissions: 11 | actions: write 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | if: github.repository_owner == 'spring-projects' 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | with: 20 | ref: docs-build 21 | fetch-depth: 1 22 | - name: Dispatch (partial build) 23 | if: github.ref_type == 'branch' 24 | env: 25 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }} 27 | - name: Dispatch (full build) 28 | if: github.ref_type == 'tag' 29 | env: 30 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) 32 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/templates/logged-out.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |

You are now logged out.

15 |
16 |
17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /samples/default-authorizationserver/samples-default-authorizationserver.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.springframework.boot" version "3.2.2" 3 | id "io.spring.dependency-management" version "1.1.0" 4 | id "java" 5 | } 6 | 7 | group = project.rootProject.group 8 | version = project.rootProject.version 9 | 10 | java { 11 | sourceCompatibility = JavaVersion.VERSION_17 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | maven { url "https://repo.spring.io/milestone" } 17 | maven { url "https://repo.spring.io/snapshot" } 18 | } 19 | 20 | dependencies { 21 | implementation "org.springframework.boot:spring-boot-starter-web" 22 | implementation "org.springframework.boot:spring-boot-starter-security" 23 | implementation project(":spring-security-oauth2-authorization-server") 24 | 25 | testImplementation "org.springframework.boot:spring-boot-starter-test" 26 | testImplementation "org.springframework.security:spring-security-test" 27 | testImplementation "org.junit.jupiter:junit-jupiter" 28 | testImplementation "net.sourceforge.htmlunit:htmlunit" 29 | } 30 | 31 | tasks.named("test") { 32 | useJUnitPlatform() 33 | } 34 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/web/LoginController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.GetMapping; 20 | 21 | /** 22 | * @author Steve Riesenberg 23 | * @since 1.1 24 | */ 25 | @Controller 26 | public class LoginController { 27 | 28 | @GetMapping("/login") 29 | public String login() { 30 | return "login"; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /samples/users-resource/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8091 3 | 4 | logging: 5 | level: 6 | org.springframework.security: INFO 7 | 8 | spring: 9 | security: 10 | oauth2: 11 | resourceserver: 12 | jwt: 13 | issuer-uri: http://localhost:9000 14 | client: 15 | registration: 16 | messaging-client-client-credentials: 17 | provider: spring 18 | client-id: messaging-client 19 | client-secret: secret 20 | authorization-grant-type: client_credentials 21 | client-name: messaging-client-client-credentials 22 | messaging-client-token-exchange: 23 | provider: spring 24 | client-id: token-client 25 | client-secret: token 26 | authorization-grant-type: urn:ietf:params:oauth:grant-type:token-exchange 27 | scope: message.read 28 | client-name: messaging-client-token-exchange 29 | provider: 30 | spring: 31 | issuer-uri: http://localhost:9000 32 | 33 | messages: 34 | base-uri: http://127.0.0.1:8090 35 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/jpa/repository/client/ClientRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.jpa.repository.client; 17 | 18 | import java.util.Optional; 19 | 20 | import sample.jpa.entity.client.Client; 21 | 22 | import org.springframework.data.jpa.repository.JpaRepository; 23 | import org.springframework.stereotype.Repository; 24 | 25 | @Repository 26 | public interface ClientRepository extends JpaRepository { 27 | Optional findByClientId(String clientId); 28 | } 29 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/index.adoc: -------------------------------------------------------------------------------- 1 | :noheader: 2 | [[top]] 3 | = Spring Authorization Server Reference 4 | 5 | [horizontal] 6 | xref:overview.adoc[Overview] :: Introduction, use cases and feature list 7 | xref:getting-help.adoc[Getting Help] :: Links to samples, questions and issues 8 | xref:getting-started.adoc[Getting Started] :: System requirements, dependencies and developing your first application 9 | xref:configuration-model.adoc[Configuration Model] :: Default configuration and customizing the configuration 10 | xref:core-model-components.adoc[Core Model / Components] :: Core domain model and component interfaces 11 | xref:protocol-endpoints.adoc[Protocol Endpoints] :: OAuth2 and OpenID Connect 1.0 protocol endpoint implementations 12 | xref:how-to.adoc[How-to Guides] :: Guides to get the most from Spring Authorization Server 13 | 14 | Joe Grandja, Steve Riesenberg 15 | 16 | Copyright © 2020 - 2023 17 | 18 | Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. 19 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/java/sample/DemoClientApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | 21 | /** 22 | * @author Joe Grandja 23 | * @since 0.0.1 24 | */ 25 | @SpringBootApplication 26 | public class DemoClientApplication { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(DemoClientApplication.class, args); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/templates/device-activated.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Success!

14 |

15 | You have successfully activated your device.
16 | Please return to your device to continue. 17 |

18 |
19 |
20 | Devices 21 |
22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /samples/messages-resource/src/main/java/sample/web/MessagesController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import org.springframework.web.bind.annotation.GetMapping; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | /** 22 | * @author Joe Grandja 23 | * @since 0.0.1 24 | */ 25 | @RestController 26 | public class MessagesController { 27 | 28 | @GetMapping("/messages") 29 | public String[] getMessages() { 30 | return new String[] {"Message 1", "Message 2", "Message 3"}; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /samples/users-resource/src/main/java/sample/UsersResourceApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | 21 | /** 22 | * @author Steve Riesenberg 23 | * @since 1.3 24 | */ 25 | @SpringBootApplication 26 | public class UsersResourceApplication { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(UsersResourceApplication.class, args); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /samples/messages-resource/src/main/java/sample/MessagesResourceApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | 21 | /** 22 | * @author Joe Grandja 23 | * @since 0.0.1 24 | */ 25 | @SpringBootApplication 26 | public class MessagesResourceApplication { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(MessagesResourceApplication.class, args); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /samples/demo-client/samples-demo-client.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.springframework.boot" version "3.2.2" 3 | id "io.spring.dependency-management" version "1.1.0" 4 | id "java" 5 | } 6 | 7 | group = project.rootProject.group 8 | version = project.rootProject.version 9 | 10 | java { 11 | sourceCompatibility = JavaVersion.VERSION_17 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | maven { url "https://repo.spring.io/milestone" } 17 | maven { url "https://repo.spring.io/snapshot" } 18 | } 19 | 20 | dependencies { 21 | implementation "org.springframework.boot:spring-boot-starter-web" 22 | implementation "org.springframework.boot:spring-boot-starter-thymeleaf" 23 | implementation "org.springframework.boot:spring-boot-starter-security" 24 | implementation "org.springframework.boot:spring-boot-starter-oauth2-client" 25 | implementation "org.springframework:spring-webflux" 26 | implementation "io.projectreactor.netty:reactor-netty" 27 | implementation "org.apache.httpcomponents.client5:httpclient5" 28 | implementation "org.webjars:webjars-locator-core" 29 | implementation "org.webjars:bootstrap:5.2.3" 30 | implementation "org.webjars:popper.js:2.9.3" 31 | implementation "org.webjars:jquery:3.6.4" 32 | } 33 | -------------------------------------------------------------------------------- /samples/default-authorizationserver/src/main/java/sample/DefaultAuthorizationServerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample; 17 | 18 | import org.springframework.boot.SpringApplication; 19 | import org.springframework.boot.autoconfigure.SpringBootApplication; 20 | 21 | /** 22 | * @author Joe Grandja 23 | * @since 0.0.1 24 | */ 25 | @SpringBootApplication 26 | public class DefaultAuthorizationServerApplication { 27 | 28 | public static void main(String[] args) { 29 | SpringApplication.run(DefaultAuthorizationServerApplication.class, args); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /samples/default-authorizationserver/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9000 3 | 4 | logging: 5 | level: 6 | org.springframework.security: trace 7 | 8 | spring: 9 | security: 10 | user: 11 | name: user1 12 | password: password 13 | oauth2: 14 | authorizationserver: 15 | client: 16 | messaging-client: 17 | registration: 18 | client-id: "messaging-client" 19 | client-secret: "{noop}secret" 20 | client-authentication-methods: 21 | - "client_secret_basic" 22 | authorization-grant-types: 23 | - "authorization_code" 24 | - "refresh_token" 25 | - "client_credentials" 26 | redirect-uris: 27 | - "http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc" 28 | - "http://127.0.0.1:8080/authorized" 29 | post-logout-redirect-uris: 30 | - "http://127.0.0.1:8080/logged-out" 31 | scopes: 32 | - "openid" 33 | - "profile" 34 | - "message.read" 35 | - "message.write" 36 | require-authorization-consent: true 37 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/java/sample/web/DefaultController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.GetMapping; 20 | 21 | /** 22 | * @author Joe Grandja 23 | * @author Dmitriy Dubson 24 | * @since 0.0.1 25 | */ 26 | @Controller 27 | public class DefaultController { 28 | 29 | @GetMapping("/") 30 | public String root() { 31 | return "redirect:/index"; 32 | } 33 | 34 | @GetMapping("/index") 35 | public String index() { 36 | return "index"; 37 | } 38 | 39 | @GetMapping("/logged-out") 40 | public String loggedOut() { 41 | return "logged-out"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/samples-demo-authorizationserver.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.springframework.boot" version "3.2.2" 3 | id "io.spring.dependency-management" version "1.1.0" 4 | id "java" 5 | } 6 | 7 | group = project.rootProject.group 8 | version = project.rootProject.version 9 | 10 | java { 11 | sourceCompatibility = JavaVersion.VERSION_17 12 | } 13 | 14 | repositories { 15 | mavenCentral() 16 | maven { url "https://repo.spring.io/milestone" } 17 | maven { url "https://repo.spring.io/snapshot" } 18 | } 19 | 20 | dependencies { 21 | implementation "org.springframework.boot:spring-boot-starter-web" 22 | implementation "org.springframework.boot:spring-boot-starter-thymeleaf" 23 | implementation "org.springframework.boot:spring-boot-starter-security" 24 | implementation "org.springframework.boot:spring-boot-starter-oauth2-client" 25 | implementation "org.springframework.boot:spring-boot-starter-jdbc" 26 | implementation project(":spring-security-oauth2-authorization-server") 27 | runtimeOnly "com.h2database:h2" 28 | 29 | testImplementation "org.springframework.boot:spring-boot-starter-test" 30 | testImplementation "org.springframework.security:spring-security-test" 31 | testImplementation "org.junit.jupiter:junit-jupiter" 32 | testImplementation "net.sourceforge.htmlunit:htmlunit" 33 | } 34 | 35 | tasks.named("test") { 36 | useJUnitPlatform() 37 | } 38 | -------------------------------------------------------------------------------- /samples/x509-certificate-generator/generated/spring-samples-trusted-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDajCCAlKgAwIBAgIIQq1ttG0H6QcwDQYJKoZIhvcNAQELBQAwWzELMAkGA1UE 3 | BhMCVVMxDzANBgNVBAoTBlNwcmluZzEXMBUGA1UECxMOU3ByaW5nIFNhbXBsZXMx 4 | IjAgBgNVBAMTGXNwcmluZy1zYW1wbGVzLXRydXN0ZWQtY2EwHhcNMjQwNDAyMTAy 5 | MDE2WhcNMjUwNDAyMTAyMDE2WjBbMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGU3By 6 | aW5nMRcwFQYDVQQLEw5TcHJpbmcgU2FtcGxlczEiMCAGA1UEAxMZc3ByaW5nLXNh 7 | bXBsZXMtdHJ1c3RlZC1jYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 8 | ALvN0oPFMEo87oy2o0u7auqIFIt3czyH+8SCJL3jUCbCNnA9THh41Bpo3tVARVTh 9 | DF2wdzL1nZdYCL5Z4eYujlDe4TyzPTioW+eMBkIt6NuDdWbAyEsDizzI6z/2217z 10 | 6xp3BowgQdUZlVd6t9LuHU7LD9IAOkXJFPbeY5/dda1txC2sOAUKF3C9g7H+QZE+ 11 | bURY6rsAYVSeyLYnwkR0J1BgHidx6hy2T1vLncrdnL/MZp2lYrk+0MB42sHef/io 12 | 0mLGj4GSzCZebh8mQTMwRI8dRVtx/qJTQHWJS3GfPPx+ewyp97sm18aJBPv9jqHk 13 | 0OYQEjb8uXbbB5uf0kfIEj0CAwEAAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV 14 | HQ4EFgQUmJZhDGV3S/o2ifi1PP+NK/oEnO4wDQYJKoZIhvcNAQELBQADggEBAJ/+ 15 | 0pxDNgZx6YlcLWx0mHWIU0T76zXSkNFvNLBy6EYgpXmZMBiQl/PmY0p4UJ6bxunt 16 | rLHJe5lQwO1vKrFssZIoZrdK2BVlANJq4rh1Pp03o7kbs2IsYiIi0AhWWFKJep+/ 17 | Iz3OJPBHDYlcazHi0bGpkxr+3eDjVx+i7F0bQjZjWBqHg7so+mx4MAA/s2BXfauT 18 | TuUWJYJH0PQHT0pmv06smI+xN/3hJJH9vioBGkw45bUxpLvGLx8IVbEl2xr4bH4f 19 | phRGI5dMjNVp+m4YpKl5UzFLAwnfUQNIJ2M3PupvlIQ4wqurCqEFU9Fp/fTI/83Q 20 | TjnuNVlObMkz9ccwL6Q= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/HashSetMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import java.util.HashSet; 19 | import java.util.Set; 20 | 21 | import com.fasterxml.jackson.annotation.JsonCreator; 22 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 23 | 24 | /** 25 | * This mixin class is used to serialize/deserialize {@link HashSet}. 26 | * 27 | * @author Steve Riesenberg 28 | * @since 0.1.2 29 | * @see HashSet 30 | */ 31 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 32 | abstract class HashSetMixin { 33 | 34 | @JsonCreator 35 | HashSetMixin(Set set) { 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/resources/templates/device-activate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |

Activation Required

15 |

You must activate this device.

16 | Activate 17 |
18 |
19 | Devices 20 |
21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/jpa/repository/authorizationconsent/AuthorizationConsentRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.jpa.repository.authorizationconsent; 17 | 18 | import java.util.Optional; 19 | 20 | import sample.jpa.entity.authorizationconsent.AuthorizationConsent; 21 | 22 | import org.springframework.data.jpa.repository.JpaRepository; 23 | import org.springframework.stereotype.Repository; 24 | 25 | @Repository 26 | public interface AuthorizationConsentRepository extends JpaRepository { 27 | Optional findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName); 28 | void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName); 29 | } 30 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/util/SpringAuthorizationServerVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.util; 17 | 18 | /** 19 | * Internal class used for serialization across Spring Authorization Server classes. 20 | * 21 | * @author Anoop Garlapati 22 | * @since 0.0.1 23 | */ 24 | public final class SpringAuthorizationServerVersion { 25 | private static final int MAJOR = 1; 26 | private static final int MINOR = 3; 27 | private static final int PATCH = 0; 28 | 29 | /** 30 | * Global Serialization value for Spring Authorization Server classes. 31 | */ 32 | public static final long SERIAL_VERSION_UID = getVersion().hashCode(); 33 | 34 | public static String getVersion() { 35 | return MAJOR + "." + MINOR + "." + PATCH; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenCustomizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.token; 17 | 18 | /** 19 | * Implementations of this interface are responsible for customizing the 20 | * OAuth 2.0 Token attributes contained within the {@link OAuth2TokenContext}. 21 | * 22 | * @author Joe Grandja 23 | * @since 0.1.0 24 | * @see OAuth2TokenContext 25 | * @param the type of the context containing the OAuth 2.0 Token attributes 26 | */ 27 | @FunctionalInterface 28 | public interface OAuth2TokenCustomizer { 29 | 30 | /** 31 | * Customize the OAuth 2.0 Token attributes. 32 | * 33 | * @param context the context containing the OAuth 2.0 Token attributes 34 | */ 35 | void customize(T context); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JwsAlgorithmMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 19 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 20 | 21 | import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; 22 | 23 | /** 24 | * This mixin class is used to serialize/deserialize {@link SignatureAlgorithm}. 25 | * 26 | * @author Joe Grandja 27 | * @since 0.1.2 28 | * @see SignatureAlgorithm 29 | */ 30 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 31 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, 32 | isGetterVisibility = JsonAutoDetect.Visibility.NONE) 33 | abstract class JwsAlgorithmMixin { 34 | } 35 | -------------------------------------------------------------------------------- /oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.spring.convention.spring-module" 3 | } 4 | 5 | dependencies { 6 | management platform(project(":spring-authorization-server-dependencies")) 7 | 8 | api "org.springframework.security:spring-security-config" 9 | api "org.springframework.security:spring-security-web" 10 | api "org.springframework.security:spring-security-oauth2-core" 11 | api "org.springframework.security:spring-security-oauth2-jose" 12 | api "org.springframework.security:spring-security-oauth2-resource-server" 13 | api("org.springframework:spring-core") { 14 | exclude group: "commons-logging", module: "commons-logging" 15 | } 16 | api "com.nimbusds:nimbus-jose-jwt" 17 | api "com.fasterxml.jackson.core:jackson-databind" 18 | 19 | optional "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" 20 | optional "org.springframework:spring-jdbc" 21 | 22 | testImplementation "org.springframework.security:spring-security-test" 23 | testImplementation "org.springframework:spring-webmvc" 24 | testImplementation "org.bouncycastle:bcpkix-jdk18on" 25 | testImplementation "org.bouncycastle:bcprov-jdk18on" 26 | testImplementation "org.junit.jupiter:junit-jupiter" 27 | testImplementation "org.assertj:assertj-core" 28 | testImplementation "org.mockito:mockito-core" 29 | testImplementation "com.jayway.jsonpath:json-path" 30 | testImplementation "com.squareup.okhttp3:mockwebserver" 31 | 32 | testRuntimeOnly "org.hsqldb:hsqldb" 33 | 34 | provided "jakarta.servlet:jakarta.servlet-api" 35 | } 36 | -------------------------------------------------------------------------------- /samples/x509-certificate-generator/generated/spring-samples-ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEBjCCAu6gAwIBAgIIpMf6rDm8a1IwDQYJKoZIhvcNAQELBQAwWzELMAkGA1UE 3 | BhMCVVMxDzANBgNVBAoTBlNwcmluZzEXMBUGA1UECxMOU3ByaW5nIFNhbXBsZXMx 4 | IjAgBgNVBAMTGXNwcmluZy1zYW1wbGVzLXRydXN0ZWQtY2EwHhcNMjQwNDAyMTAy 5 | MDE2WhcNMjUwNDAyMTAyMDE2WjBTMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGU3By 6 | aW5nMRcwFQYDVQQLEw5TcHJpbmcgU2FtcGxlczEaMBgGA1UEAxMRc3ByaW5nLXNh 7 | bXBsZXMtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgOMG5PXY+ 8 | jOUp07/wltx0bPN4JiFhCcK35fE6nzxBQJ/gtR6djAjrEOtJh5uJB8k7kF9bKMU3 9 | Eb3kBwO/vR6pemKVUMFA1wyMthnrR36D+Q2MwhWG6BGGTkQ9GSBv6JSWZtQeJBHT 10 | P6XFsbtMySFSamqdTnXjspv4hVAs/gNYyHoAfR67I0L1mSdlnHo2R48+eYA+7Kqt 11 | d4IuW/nJY4ZCYNksOEemhY2ck7VGmd87PgwibXLVmbUob7UOdn4j7x4rqUgaHl83 12 | eBnu6W+xYTM1Cjc/jITMf3dXejEr9/68CNIxlnB02cZnr1JE6Bw6yIFMcrNaCYOd 13 | ODt4zaViZBzRAgMBAAGjgdUwgdIwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B 14 | Af8EBAMCAQYwgYwGA1UdIwSBhDCBgYAUmJZhDGV3S/o2ifi1PP+NK/oEnO6hX6Rd 15 | MFsxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKEwZTcHJpbmcxFzAVBgNVBAsTDlNwcmlu 16 | ZyBTYW1wbGVzMSIwIAYDVQQDExlzcHJpbmctc2FtcGxlcy10cnVzdGVkLWNhgghC 17 | rW20bQfpBzAdBgNVHQ4EFgQUbTwdOttQ8rYcCuW95y5ZEdW0CsEwDQYJKoZIhvcN 18 | AQELBQADggEBAKW0Z0oq+jGvjfrKhxqxW3mfEZT+rrjkZXXC0f4YtH5HhddM/Jk/ 19 | kB3p3OWoUS1b9F/jkFVZjihL7iKhpy8XvukRI6cm5PNbY9PN0hzmf0dg+3W61R1Q 20 | DB0rAJMGmhMw7j6mpZVcZS15gxP3pR/4JCM2xjDrpgGmwAJDPLD2b2tTX5Zr66mF 21 | fZ0pl414990wBwDDPi+vlJ345fcwATvLeYxLykXCpCiLDziW81PZ/NnMXhfL75QB 22 | Z9Pfg9Ose2esdJ+FOYPhZr85BQxS46DBtAsUZHjQXBO7/xbahv3euKV0jlJkzkre 23 | fqMfdOOCBi7vUpqCW7c0n2cIEwJiqO0HGU4= 24 | -----END CERTIFICATE----- 25 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | } 5 | } 6 | 7 | plugins { 8 | id "com.gradle.enterprise" version "3.16.2" 9 | id "io.spring.ge.conventions" version "0.0.15" 10 | } 11 | 12 | dependencyResolutionManagement { 13 | repositories { 14 | mavenCentral() 15 | } 16 | } 17 | 18 | rootProject.name = "spring-authorization-server" 19 | 20 | def buildFiles = fileTree(rootDir) { 21 | def excludes = gradle.startParameter.projectProperties.get("excludeProjects")?.split(",") 22 | include "**/*.gradle", "**/*.gradle.kts" 23 | exclude "build", "**/gradle", "settings.gradle", "buildSrc", "/build.gradle", ".*", "out" 24 | if (excludes) { 25 | exclude excludes 26 | } 27 | } 28 | 29 | buildFiles.forEach { buildFile -> 30 | def isDefaultName = buildFile.name == "build.gradle" || buildFile.name == "build.gradle.kts" 31 | def isKotlin = buildFile.name.endsWith ".kts" 32 | if (isDefaultName) { 33 | def buildFilePath = buildFile.parentFile.absolutePath 34 | def projectPath = buildFilePath.replace((String) rootDir.absolutePath, "").replace(File.separator, ":") 35 | include projectPath 36 | } else { 37 | def projectName 38 | if (isKotlin) { 39 | projectName = buildFile.name.replace(".gradle.kts", "") 40 | } else { 41 | projectName = buildFile.name.replace(".gradle", "") 42 | } 43 | 44 | 45 | def projectPath = ":$projectName" 46 | include projectPath 47 | 48 | def project = findProject(projectPath) 49 | project.name = projectName 50 | project.projectDir = buildFile.parentFile 51 | project.buildFileName = buildFile.name 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/customclaims/CustomClaimsConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.customclaims; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; 21 | import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; 22 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; 23 | 24 | @Configuration 25 | public class CustomClaimsConfiguration { 26 | @Bean 27 | public OAuth2TokenCustomizer jwtTokenCustomizer() { 28 | return (context) -> { 29 | if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { 30 | context.getClaims().claims((claims) -> { 31 | claims.put("claim-1", "value-1"); 32 | claims.put("claim-2", "value-2"); 33 | }); 34 | } 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/getting-help.adoc: -------------------------------------------------------------------------------- 1 | [[getting-help]] 2 | = Getting Help 3 | :page-section-summary-toc: 1 4 | 5 | [[community]] 6 | == Community 7 | 8 | Welcome to the https://docs.spring.io/spring-security/reference/community.html[Spring Security Community]. 9 | Spring Authorization Server is an open source project led by the Spring Security team. 10 | If you need help with Spring Authorization Server, we are here to help. 11 | 12 | [[resources]] 13 | == Resources 14 | 15 | The following are some of the best ways to get help: 16 | 17 | * Try the xref:how-to.adoc[How-to guides]. They provide solutions to the most common questions. 18 | * Learn the Spring Security basics that Spring Authorization Server builds on. If you are starting out with Spring Security, check the https://spring.io/projects/spring-security#learn[reference documentation] or try one of the https://github.com/spring-projects/spring-security-samples[samples]. 19 | * Read through xref:index.adoc[this documentation]. 20 | * Try one of our many https://github.com/spring-projects/spring-authorization-server/tree/main/samples[sample applications]. 21 | * Ask a question on Stack Overflow with the https://stackoverflow.com/questions/tagged/spring-authorization-server[`spring-authorization-server`] tag. 22 | * Report bugs and enhancement requests on https://github.com/spring-projects/spring-authorization-server/issues[GitHub]. 23 | 24 | NOTE: Spring Authorization Server is open source, including the documentation. If you find problems with the docs or if you want to improve them, please https://github.com/spring-projects/spring-authorization-server[get involved]. 25 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/web/DeviceController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import org.springframework.stereotype.Controller; 19 | import org.springframework.web.bind.annotation.GetMapping; 20 | import org.springframework.web.bind.annotation.RequestParam; 21 | 22 | /** 23 | * @author Steve Riesenberg 24 | * @since 1.1 25 | */ 26 | @Controller 27 | public class DeviceController { 28 | 29 | @GetMapping("/activate") 30 | public String activate(@RequestParam(value = "user_code", required = false) String userCode) { 31 | if (userCode != null) { 32 | return "redirect:/oauth2/device_verification?user_code=" + userCode; 33 | } 34 | return "device-activate"; 35 | } 36 | 37 | @GetMapping("/activated") 38 | public String activated() { 39 | return "device-activated"; 40 | } 41 | 42 | @GetMapping(value = "/", params = "success") 43 | public String success() { 44 | return "device-activated"; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/userinfo/jwt/JwtTokenCustomizerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.userinfo.jwt; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; 21 | import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; 22 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; 23 | 24 | @Configuration 25 | public class JwtTokenCustomizerConfig { 26 | 27 | // @formatter:off 28 | @Bean 29 | public OAuth2TokenCustomizer tokenCustomizer() { 30 | return (context) -> { 31 | if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { 32 | context.getClaims().claims((claims) -> { 33 | claims.put("claim-1", "value-1"); 34 | claims.put("claim-2", "value-2"); 35 | }); 36 | } 37 | }; 38 | } 39 | // @formatter:on 40 | 41 | } 42 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwtClaimsSets.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.jwt; 17 | 18 | import java.time.Instant; 19 | import java.time.temporal.ChronoUnit; 20 | import java.util.Collections; 21 | 22 | /** 23 | * @author Joe Grandja 24 | */ 25 | public final class TestJwtClaimsSets { 26 | 27 | private TestJwtClaimsSets() { 28 | } 29 | 30 | public static JwtClaimsSet.Builder jwtClaimsSet() { 31 | String issuer = "https://provider.com"; 32 | Instant issuedAt = Instant.now(); 33 | Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); 34 | 35 | // @formatter:off 36 | return JwtClaimsSet.builder() 37 | .issuer(issuer) 38 | .subject("subject") 39 | .audience(Collections.singletonList("client-1")) 40 | .issuedAt(issuedAt) 41 | .notBefore(issuedAt) 42 | .expiresAt(expiresAt) 43 | .id("jti") 44 | .claim("custom-claim-name", "custom-claim-value"); 45 | // @formatter:on 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/templates/device-activate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 |
11 |
12 |
13 |

Device Activation

14 |

Enter the activation code to authorize the device.

15 |
16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |
28 | Devices 29 |
30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/java/sample/authorization/OAuth2DeviceGrantRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.authorization; 17 | 18 | import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest; 19 | import org.springframework.security.oauth2.client.registration.ClientRegistration; 20 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 21 | import org.springframework.util.Assert; 22 | 23 | /** 24 | * @author Steve Riesenberg 25 | * @since 1.1 26 | */ 27 | public final class OAuth2DeviceGrantRequest extends AbstractOAuth2AuthorizationGrantRequest { 28 | 29 | private final String deviceCode; 30 | 31 | public OAuth2DeviceGrantRequest(ClientRegistration clientRegistration, String deviceCode) { 32 | super(AuthorizationGrantType.DEVICE_CODE, clientRegistration); 33 | Assert.hasText(deviceCode, "deviceCode cannot be empty"); 34 | this.deviceCode = deviceCode; 35 | } 36 | 37 | public String getDeviceCode() { 38 | return this.deviceCode; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import java.util.Collections; 19 | import java.util.Map; 20 | 21 | import com.fasterxml.jackson.annotation.JsonCreator; 22 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 23 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 24 | 25 | /** 26 | * This mixin class is used to serialize/deserialize 27 | * {@link Collections#unmodifiableMap(Map)}. It also registers a custom deserializer 28 | * {@link UnmodifiableMapDeserializer}. 29 | * 30 | * @author Joe Grandja 31 | * @since 0.1.2 32 | * @see Collections#unmodifiableMap(Map) 33 | * @see UnmodifiableMapDeserializer 34 | */ 35 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 36 | @JsonDeserialize(using = UnmodifiableMapDeserializer.class) 37 | abstract class UnmodifiableMapMixin { 38 | 39 | @JsonCreator 40 | UnmodifiableMapMixin(Map map) { 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 9443 3 | ssl: 4 | bundle: demo-authorizationserver 5 | client-auth: want 6 | 7 | spring: 8 | ssl: 9 | bundle: 10 | jks: 11 | demo-authorizationserver: 12 | key: 13 | alias: demo-authorizationserver-sample 14 | password: password 15 | keystore: 16 | location: classpath:keystore.p12 17 | password: password 18 | type: PKCS12 19 | truststore: 20 | location: classpath:keystore.p12 21 | password: password 22 | type: PKCS12 23 | security: 24 | oauth2: 25 | client: 26 | registration: 27 | google-idp: 28 | provider: google 29 | client-id: ${GOOGLE_CLIENT_ID:google-client-id} 30 | client-secret: ${GOOGLE_CLIENT_SECRET:google-client-secret} 31 | scope: openid, https://www.googleapis.com/auth/userinfo.profile, https://www.googleapis.com/auth/userinfo.email 32 | client-name: Sign in with Google 33 | github-idp: 34 | provider: github 35 | client-id: ${GITHUB_CLIENT_ID:github-client-id} 36 | client-secret: ${GITHUB_CLIENT_SECRET:github-client-secret} 37 | scope: user:email, read:user 38 | client-name: Sign in with GitHub 39 | provider: 40 | google: 41 | user-name-attribute: email 42 | github: 43 | user-name-attribute: login 44 | 45 | logging: 46 | level: 47 | root: INFO 48 | org.springframework.web: INFO 49 | org.springframework.security: INFO 50 | org.springframework.security.oauth2: INFO 51 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/extgrant/CustomCodeGrantAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.extgrant; 17 | 18 | import java.util.Map; 19 | 20 | import org.springframework.lang.Nullable; 21 | import org.springframework.security.core.Authentication; 22 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 23 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken; 24 | import org.springframework.util.Assert; 25 | 26 | public class CustomCodeGrantAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { 27 | private final String code; 28 | 29 | public CustomCodeGrantAuthenticationToken(String code, Authentication clientPrincipal, 30 | @Nullable Map additionalParameters) { 31 | super(new AuthorizationGrantType("urn:ietf:params:oauth:grant-type:custom_code"), 32 | clientPrincipal, additionalParameters); 33 | Assert.hasText(code, "code cannot be empty"); 34 | this.code = code; 35 | } 36 | 37 | public String getCode() { 38 | return this.code; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2TokenFormatMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 19 | import com.fasterxml.jackson.annotation.JsonCreator; 20 | import com.fasterxml.jackson.annotation.JsonProperty; 21 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 22 | 23 | import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat; 24 | 25 | /** 26 | * This mixin class is used to serialize/deserialize {@link OAuth2TokenFormat}. 27 | * 28 | * @author Joe Grandja 29 | * @since 0.2.3 30 | * @see OAuth2TokenFormat 31 | */ 32 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 33 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, 34 | isGetterVisibility = JsonAutoDetect.Visibility.NONE) 35 | abstract class OAuth2TokenFormatMixin { 36 | 37 | @JsonCreator 38 | OAuth2TokenFormatMixin(@JsonProperty("value") String value) { 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /samples/messages-resource/src/main/java/sample/config/TomcatServerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.config; 17 | 18 | import org.apache.catalina.connector.Connector; 19 | 20 | import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; 21 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | 25 | /** 26 | * @author Joe Grandja 27 | * @since 1.3 28 | */ 29 | @Configuration(proxyBeanMethods = false) 30 | public class TomcatServerConfig { 31 | 32 | @Bean 33 | public WebServerFactoryCustomizer connectorCustomizer() { 34 | return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createHttpConnector()); 35 | } 36 | 37 | private Connector createHttpConnector() { 38 | Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); 39 | connector.setScheme("http"); 40 | connector.setPort(8090); 41 | connector.setSecure(false); 42 | connector.setRedirectPort(8443); 43 | return connector; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationCode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization; 17 | 18 | import java.time.Instant; 19 | 20 | import org.springframework.security.oauth2.core.AbstractOAuth2Token; 21 | 22 | /** 23 | * An implementation of an {@link AbstractOAuth2Token} 24 | * representing an OAuth 2.0 Authorization Code Grant. 25 | * 26 | * @author Joe Grandja 27 | * @since 0.0.3 28 | * @see AbstractOAuth2Token 29 | * @see Section 4.1 Authorization Code Grant 30 | */ 31 | public class OAuth2AuthorizationCode extends AbstractOAuth2Token { 32 | 33 | /** 34 | * Constructs an {@code OAuth2AuthorizationCode} using the provided parameters. 35 | * @param tokenValue the token value 36 | * @param issuedAt the time at which the token was issued 37 | * @param expiresAt the time at which the token expires 38 | */ 39 | public OAuth2AuthorizationCode(String tokenValue, Instant issuedAt, Instant expiresAt) { 40 | super(tokenValue, issuedAt, expiresAt); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/userinfo/idtoken/IdTokenCustomizerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.userinfo.idtoken; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.oauth2.core.oidc.OidcUserInfo; 21 | import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; 22 | import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; 23 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; 24 | 25 | @Configuration 26 | public class IdTokenCustomizerConfig { 27 | 28 | // @formatter:off 29 | @Bean // <1> 30 | public OAuth2TokenCustomizer tokenCustomizer( 31 | OidcUserInfoService userInfoService) { 32 | return (context) -> { 33 | if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) { 34 | OidcUserInfo userInfo = userInfoService.loadUser( // <2> 35 | context.getPrincipal().getName()); 36 | context.getClaims().claims(claims -> 37 | claims.putAll(userInfo.getClaims())); 38 | } 39 | }; 40 | } 41 | // @formatter:on 42 | 43 | } 44 | -------------------------------------------------------------------------------- /etc/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeActorTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.authentication; 17 | 18 | import java.util.Map; 19 | 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; 24 | 25 | /** 26 | * Tests for {@link OAuth2TokenExchangeActor}. 27 | * 28 | * @author Steve Riesenberg 29 | */ 30 | public class OAuth2TokenExchangeActorTests { 31 | 32 | @Test 33 | public void constructorWhenClaimsNullThenThrowIllegalArgumentException() { 34 | // @formatter:off 35 | assertThatIllegalArgumentException() 36 | .isThrownBy(() -> new OAuth2TokenExchangeActor(null)) 37 | .withMessage("claims cannot be null"); 38 | // @formatter:on 39 | } 40 | 41 | @Test 42 | public void constructorWhenRequiredParametersThenCreated() { 43 | Map claims = Map.of("claim1", "value1"); 44 | OAuth2TokenExchangeActor actor = new OAuth2TokenExchangeActor(claims); 45 | assertThat(actor.getClaims()).isEqualTo(claims); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /samples/users-resource/src/main/java/sample/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.config; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.config.Customizer; 21 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 22 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 23 | import org.springframework.security.web.SecurityFilterChain; 24 | 25 | /** 26 | * @author Steve Riesenberg 27 | * @since 1.3 28 | */ 29 | @Configuration 30 | @EnableWebSecurity 31 | public class SecurityConfig { 32 | 33 | @Bean 34 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 35 | // @formatter:off 36 | http 37 | .securityMatcher("/user/**") 38 | .authorizeHttpRequests((authorize) -> authorize 39 | .requestMatchers("/user/**").hasAuthority("SCOPE_user.read") 40 | ) 41 | .oauth2ResourceServer((oauth2ResourceServer) -> oauth2ResourceServer 42 | .jwt(Customizer.withDefaults()) 43 | ) 44 | .oauth2Client(Customizer.withDefaults()); 45 | // @formatter:on 46 | 47 | return http.build(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE oauth2Authorization ( 2 | id varchar(100) NOT NULL, 3 | registeredClientId varchar(100) NOT NULL, 4 | principalName varchar(200) NOT NULL, 5 | authorizationGrantType varchar(100) NOT NULL, 6 | authorizedScopes varchar(1000) DEFAULT NULL, 7 | attributes varchar(4000) DEFAULT NULL, 8 | state varchar(500) DEFAULT NULL, 9 | authorizationCodeValue varchar(1000) DEFAULT NULL, 10 | authorizationCodeIssuedAt timestamp DEFAULT NULL, 11 | authorizationCodeExpiresAt timestamp DEFAULT NULL, 12 | authorizationCodeMetadata varchar(2000) DEFAULT NULL, 13 | accessTokenValue varchar(1000) DEFAULT NULL, 14 | accessTokenIssuedAt timestamp DEFAULT NULL, 15 | accessTokenExpiresAt timestamp DEFAULT NULL, 16 | accessTokenMetadata varchar(2000) DEFAULT NULL, 17 | accessTokenType varchar(100) DEFAULT NULL, 18 | accessTokenScopes varchar(1000) DEFAULT NULL, 19 | oidcIdTokenValue varchar(1000) DEFAULT NULL, 20 | oidcIdTokenIssuedAt timestamp DEFAULT NULL, 21 | oidcIdTokenExpiresAt timestamp DEFAULT NULL, 22 | oidcIdTokenMetadata varchar(2000) DEFAULT NULL, 23 | refreshTokenValue varchar(1000) DEFAULT NULL, 24 | refreshTokenIssuedAt timestamp DEFAULT NULL, 25 | refreshTokenExpiresAt timestamp DEFAULT NULL, 26 | refreshTokenMetadata varchar(2000) DEFAULT NULL, 27 | userCodeValue varchar(1000) DEFAULT NULL, 28 | userCodeIssuedAt timestamp DEFAULT NULL, 29 | userCodeExpiresAt timestamp DEFAULT NULL, 30 | userCodeMetadata varchar(2000) DEFAULT NULL, 31 | deviceCodeValue varchar(1000) DEFAULT NULL, 32 | deviceCodeIssuedAt timestamp DEFAULT NULL, 33 | deviceCodeExpiresAt timestamp DEFAULT NULL, 34 | deviceCodeMetadata varchar(2000) DEFAULT NULL, 35 | PRIMARY KEY (id) 36 | ); 37 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/resources/org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema-clob-data-type.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE oauth2_authorization ( 2 | id varchar(100) NOT NULL, 3 | registered_client_id varchar(100) NOT NULL, 4 | principal_name varchar(200) NOT NULL, 5 | authorization_grant_type varchar(100) NOT NULL, 6 | authorized_scopes varchar(1000) DEFAULT NULL, 7 | attributes varchar(4000) DEFAULT NULL, 8 | state varchar(500) DEFAULT NULL, 9 | authorization_code_value clob DEFAULT NULL, 10 | authorization_code_issued_at timestamp DEFAULT NULL, 11 | authorization_code_expires_at timestamp DEFAULT NULL, 12 | authorization_code_metadata varchar(2000) DEFAULT NULL, 13 | access_token_value clob DEFAULT NULL, 14 | access_token_issued_at timestamp DEFAULT NULL, 15 | access_token_expires_at timestamp DEFAULT NULL, 16 | access_token_metadata varchar(2000) DEFAULT NULL, 17 | access_token_type varchar(100) DEFAULT NULL, 18 | access_token_scopes varchar(1000) DEFAULT NULL, 19 | oidc_id_token_value clob DEFAULT NULL, 20 | oidc_id_token_issued_at timestamp DEFAULT NULL, 21 | oidc_id_token_expires_at timestamp DEFAULT NULL, 22 | oidc_id_token_metadata varchar(2000) DEFAULT NULL, 23 | refresh_token_value clob DEFAULT NULL, 24 | refresh_token_issued_at timestamp DEFAULT NULL, 25 | refresh_token_expires_at timestamp DEFAULT NULL, 26 | refresh_token_metadata varchar(2000) DEFAULT NULL, 27 | user_code_value clob DEFAULT NULL, 28 | user_code_issued_at timestamp DEFAULT NULL, 29 | user_code_expires_at timestamp DEFAULT NULL, 30 | user_code_metadata varchar(2000) DEFAULT NULL, 31 | device_code_value clob DEFAULT NULL, 32 | device_code_issued_at timestamp DEFAULT NULL, 33 | device_code_expires_at timestamp DEFAULT NULL, 34 | device_code_metadata varchar(2000) DEFAULT NULL, 35 | PRIMARY KEY (id) 36 | ); 37 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/context/TestAuthorizationServerContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.context; 17 | 18 | import java.util.function.Supplier; 19 | 20 | import org.springframework.lang.Nullable; 21 | import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; 22 | 23 | /** 24 | * @author Joe Grandja 25 | */ 26 | public class TestAuthorizationServerContext implements AuthorizationServerContext { 27 | private final AuthorizationServerSettings authorizationServerSettings; 28 | private final Supplier issuerSupplier; 29 | 30 | public TestAuthorizationServerContext(AuthorizationServerSettings authorizationServerSettings, @Nullable Supplier issuerSupplier) { 31 | this.authorizationServerSettings = authorizationServerSettings; 32 | this.issuerSupplier = issuerSupplier; 33 | } 34 | 35 | @Override 36 | public String getIssuer() { 37 | return this.issuerSupplier != null ? 38 | this.issuerSupplier.get() : 39 | getAuthorizationServerSettings().getIssuer(); 40 | } 41 | 42 | @Override 43 | public AuthorizationServerSettings getAuthorizationServerSettings() { 44 | return this.authorizationServerSettings; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/config/TomcatServerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.config; 17 | 18 | import org.apache.catalina.connector.Connector; 19 | 20 | import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; 21 | import org.springframework.boot.web.server.WebServerFactoryCustomizer; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | import org.springframework.context.annotation.Profile; 25 | 26 | /** 27 | * @author Joe Grandja 28 | * @since 1.3 29 | */ 30 | @Profile("!test") // Exclude this from DemoAuthorizationServerApplicationTests and DemoAuthorizationServerConsentTests 31 | @Configuration(proxyBeanMethods = false) 32 | public class TomcatServerConfig { 33 | 34 | @Bean 35 | public WebServerFactoryCustomizer connectorCustomizer() { 36 | return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createHttpConnector()); 37 | } 38 | 39 | private Connector createHttpConnector() { 40 | Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL); 41 | connector.setScheme("http"); 42 | connector.setPort(9000); 43 | connector.setSecure(false); 44 | connector.setRedirectPort(9443); 45 | return connector; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | import org.junit.jupiter.api.Test; 21 | 22 | import org.springframework.core.Ordered; 23 | import org.springframework.core.annotation.OrderUtils; 24 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 25 | import org.springframework.util.ClassUtils; 26 | 27 | import static org.assertj.core.api.Assertions.assertThat; 28 | 29 | /** 30 | * Tests for {@link OAuth2AuthorizationServerConfiguration}. 31 | * 32 | * @author Joe Grandja 33 | */ 34 | public class OAuth2AuthorizationServerConfigurationTests { 35 | 36 | @Test 37 | public void assertOrderHighestPrecedence() { 38 | Method authorizationServerSecurityFilterChainMethod = 39 | ClassUtils.getMethod( 40 | OAuth2AuthorizationServerConfiguration.class, 41 | "authorizationServerSecurityFilterChain", 42 | HttpSecurity.class); 43 | Integer order = OrderUtils.getOrder(authorizationServerSecurityFilterChainMethod); 44 | assertThat(order).isEqualTo(Ordered.HIGHEST_PRECEDENCE); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 19 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 20 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 21 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 22 | 23 | import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; 24 | 25 | /** 26 | * This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}. 27 | * It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}. 28 | * 29 | * @author Joe Grandja 30 | * @since 0.1.2 31 | * @see OAuth2AuthorizationRequest 32 | * @see OAuth2AuthorizationRequestDeserializer 33 | */ 34 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 35 | @JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class) 36 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, 37 | isGetterVisibility = JsonAutoDetect.Visibility.NONE) 38 | @JsonIgnoreProperties(ignoreUnknown = true) 39 | abstract class OAuth2AuthorizationRequestMixin { 40 | 41 | } 42 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/authentication/DeviceClientAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.authentication; 17 | 18 | import java.util.Map; 19 | 20 | import org.springframework.lang.Nullable; 21 | import org.springframework.security.core.Transient; 22 | import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 23 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; 24 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 25 | 26 | /** 27 | * @author Joe Grandja 28 | * @author Steve Riesenberg 29 | * @since 1.1 30 | */ 31 | @Transient 32 | public class DeviceClientAuthenticationToken extends OAuth2ClientAuthenticationToken { 33 | 34 | public DeviceClientAuthenticationToken(String clientId, ClientAuthenticationMethod clientAuthenticationMethod, 35 | @Nullable Object credentials, @Nullable Map additionalParameters) { 36 | super(clientId, clientAuthenticationMethod, credentials, additionalParameters); 37 | } 38 | 39 | public DeviceClientAuthenticationToken(RegisteredClient registeredClient, ClientAuthenticationMethod clientAuthenticationMethod, 40 | @Nullable Object credentials) { 41 | super(registeredClient, clientAuthenticationMethod, credentials); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/DurationMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import java.time.Duration; 19 | 20 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 21 | import com.fasterxml.jackson.annotation.JsonCreator; 22 | import com.fasterxml.jackson.annotation.JsonGetter; 23 | import com.fasterxml.jackson.annotation.JsonProperty; 24 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 25 | 26 | /** 27 | * This mixin class is used to serialize/deserialize {@link Duration}. 28 | * 29 | * @author Joe Grandja 30 | * @since 0.1.2 31 | * @see Duration 32 | */ 33 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 34 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, 35 | isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE, 36 | creatorVisibility = JsonAutoDetect.Visibility.NONE) 37 | abstract class DurationMixin { 38 | 39 | @JsonCreator 40 | static void ofSeconds(@JsonProperty("seconds") long seconds, @JsonProperty("nano") long nanoAdjustment) { 41 | } 42 | 43 | @JsonGetter("seconds") 44 | abstract long getSeconds(); 45 | 46 | @JsonGetter("nano") 47 | abstract int getNano(); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AbstractOAuth2Configurer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; 17 | 18 | import org.springframework.security.config.annotation.ObjectPostProcessor; 19 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 20 | import org.springframework.security.web.util.matcher.RequestMatcher; 21 | 22 | /** 23 | * Base configurer for an OAuth 2.0 component (e.g. protocol endpoint). 24 | * 25 | * @author Joe Grandja 26 | * @since 0.1.2 27 | */ 28 | abstract class AbstractOAuth2Configurer { 29 | private final ObjectPostProcessor objectPostProcessor; 30 | 31 | AbstractOAuth2Configurer(ObjectPostProcessor objectPostProcessor) { 32 | this.objectPostProcessor = objectPostProcessor; 33 | } 34 | 35 | abstract void init(HttpSecurity httpSecurity); 36 | 37 | abstract void configure(HttpSecurity httpSecurity); 38 | 39 | abstract RequestMatcher getRequestMatcher(); 40 | 41 | protected final T postProcess(T object) { 42 | return (T) this.objectPostProcessor.postProcess(object); 43 | } 44 | 45 | protected final ObjectPostProcessor getObjectPostProcessor() { 46 | return this.objectPostProcessor; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/resources/org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql: -------------------------------------------------------------------------------- 1 | /* 2 | IMPORTANT: 3 | If using PostgreSQL, update ALL columns defined with 'blob' to 'text', 4 | as PostgreSQL does not support the 'blob' data type. 5 | */ 6 | CREATE TABLE oauth2_authorization ( 7 | id varchar(100) NOT NULL, 8 | registered_client_id varchar(100) NOT NULL, 9 | principal_name varchar(200) NOT NULL, 10 | authorization_grant_type varchar(100) NOT NULL, 11 | authorized_scopes varchar(1000) DEFAULT NULL, 12 | attributes blob DEFAULT NULL, 13 | state varchar(500) DEFAULT NULL, 14 | authorization_code_value blob DEFAULT NULL, 15 | authorization_code_issued_at timestamp DEFAULT NULL, 16 | authorization_code_expires_at timestamp DEFAULT NULL, 17 | authorization_code_metadata blob DEFAULT NULL, 18 | access_token_value blob DEFAULT NULL, 19 | access_token_issued_at timestamp DEFAULT NULL, 20 | access_token_expires_at timestamp DEFAULT NULL, 21 | access_token_metadata blob DEFAULT NULL, 22 | access_token_type varchar(100) DEFAULT NULL, 23 | access_token_scopes varchar(1000) DEFAULT NULL, 24 | oidc_id_token_value blob DEFAULT NULL, 25 | oidc_id_token_issued_at timestamp DEFAULT NULL, 26 | oidc_id_token_expires_at timestamp DEFAULT NULL, 27 | oidc_id_token_metadata blob DEFAULT NULL, 28 | refresh_token_value blob DEFAULT NULL, 29 | refresh_token_issued_at timestamp DEFAULT NULL, 30 | refresh_token_expires_at timestamp DEFAULT NULL, 31 | refresh_token_metadata blob DEFAULT NULL, 32 | user_code_value blob DEFAULT NULL, 33 | user_code_issued_at timestamp DEFAULT NULL, 34 | user_code_expires_at timestamp DEFAULT NULL, 35 | user_code_metadata blob DEFAULT NULL, 36 | device_code_value blob DEFAULT NULL, 37 | device_code_issued_at timestamp DEFAULT NULL, 38 | device_code_expires_at timestamp DEFAULT NULL, 39 | device_code_metadata blob DEFAULT NULL, 40 | PRIMARY KEY (id) 41 | ); 42 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/registration/ClientConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.registration; 17 | 18 | import java.util.UUID; 19 | 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 23 | import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 24 | import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository; 25 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 26 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; 27 | 28 | @Configuration 29 | public class ClientConfig { 30 | 31 | @Bean 32 | public RegisteredClientRepository registeredClientRepository() { 33 | RegisteredClient registrarClient = RegisteredClient.withId(UUID.randomUUID().toString()) 34 | .clientId("registrar-client") 35 | .clientSecret("{noop}secret") 36 | .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) 37 | .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) // <1> 38 | .scope("client.create") // <2> 39 | .scope("client.read") // <3> 40 | .build(); 41 | 42 | return new InMemoryRegisteredClientRepository(registrarClient); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2TokenExchangeActorMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.security.oauth2.server.authorization.jackson2; 18 | 19 | import java.util.Map; 20 | 21 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 22 | import com.fasterxml.jackson.annotation.JsonCreator; 23 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 24 | import com.fasterxml.jackson.annotation.JsonProperty; 25 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 26 | 27 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor; 28 | 29 | /** 30 | * This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeActor}. 31 | * 32 | * @author Steve Riesenberg 33 | * @since 1.3 34 | * @see OAuth2TokenExchangeActor 35 | */ 36 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 37 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, 38 | isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) 39 | @JsonIgnoreProperties(ignoreUnknown = true) 40 | abstract class OAuth2TokenExchangeActorMixin { 41 | 42 | @JsonCreator 43 | OAuth2TokenExchangeActorMixin(@JsonProperty("claims") Map claims) { 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/web/DefaultErrorController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import jakarta.servlet.RequestDispatcher; 19 | import jakarta.servlet.http.HttpServletRequest; 20 | 21 | import org.springframework.boot.web.servlet.error.ErrorController; 22 | import org.springframework.stereotype.Controller; 23 | import org.springframework.ui.Model; 24 | import org.springframework.util.StringUtils; 25 | import org.springframework.web.bind.annotation.RequestMapping; 26 | 27 | /** 28 | * @author Steve Riesenberg 29 | * @since 1.1 30 | */ 31 | @Controller 32 | public class DefaultErrorController implements ErrorController { 33 | 34 | @RequestMapping("/error") 35 | public String handleError(Model model, HttpServletRequest request) { 36 | String errorMessage = getErrorMessage(request); 37 | if (errorMessage.startsWith("[access_denied]")) { 38 | model.addAttribute("errorTitle", "Access Denied"); 39 | model.addAttribute("errorMessage", "You have denied access."); 40 | } else { 41 | model.addAttribute("errorTitle", "Error"); 42 | model.addAttribute("errorMessage", errorMessage); 43 | } 44 | return "error"; 45 | } 46 | 47 | private String getErrorMessage(HttpServletRequest request) { 48 | String errorMessage = (String) request.getAttribute(RequestDispatcher.ERROR_MESSAGE); 49 | return StringUtils.hasText(errorMessage) ? errorMessage : ""; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwsHeaders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.jwt; 17 | 18 | import java.util.Arrays; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; 23 | 24 | /** 25 | * @author Joe Grandja 26 | */ 27 | public final class TestJwsHeaders { 28 | 29 | private TestJwsHeaders() { 30 | } 31 | 32 | public static JwsHeader.Builder jwsHeader() { 33 | return jwsHeader(SignatureAlgorithm.RS256); 34 | } 35 | 36 | public static JwsHeader.Builder jwsHeader(SignatureAlgorithm signatureAlgorithm) { 37 | // @formatter:off 38 | return JwsHeader.with(signatureAlgorithm) 39 | .jwkSetUrl("https://provider.com/oauth2/jwks") 40 | .jwk(rsaJwk()) 41 | .keyId("keyId") 42 | .x509Url("https://provider.com/oauth2/x509") 43 | .x509CertificateChain(Arrays.asList("x509Cert1", "x509Cert2")) 44 | .x509SHA1Thumbprint("x509SHA1Thumbprint") 45 | .x509SHA256Thumbprint("x509SHA256Thumbprint") 46 | .type("JWT") 47 | .contentType("jwt-content-type") 48 | .header("custom-header-name", "custom-header-value"); 49 | // @formatter:on 50 | } 51 | 52 | private static Map rsaJwk() { 53 | Map rsaJwk = new HashMap<>(); 54 | rsaJwk.put("kty", "RSA"); 55 | rsaJwk.put("n", "modulus"); 56 | rsaJwk.put("e", "exponent"); 57 | return rsaJwk; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/TestingAuthenticationTokenMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import java.util.List; 19 | 20 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 21 | import com.fasterxml.jackson.annotation.JsonCreator; 22 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 23 | import com.fasterxml.jackson.annotation.JsonProperty; 24 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 25 | 26 | import org.springframework.security.authentication.TestingAuthenticationToken; 27 | import org.springframework.security.core.GrantedAuthority; 28 | 29 | /** 30 | * This mixin class is used to serialize/deserialize {@link TestingAuthenticationToken}. 31 | * 32 | * @author Steve Riesenberg 33 | * @since 0.1.2 34 | * @see TestingAuthenticationToken 35 | */ 36 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 37 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, 38 | isGetterVisibility = JsonAutoDetect.Visibility.NONE) 39 | @JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true) 40 | public class TestingAuthenticationTokenMixin { 41 | 42 | @JsonCreator 43 | TestingAuthenticationTokenMixin(@JsonProperty("principal") Object principal, 44 | @JsonProperty("credentials") Object credentials, 45 | @JsonProperty("authorities") List authorities) { 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/federation/UserRepositoryOAuth2UserHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.federation; 17 | 18 | // tag::imports[] 19 | import java.util.Map; 20 | import java.util.concurrent.ConcurrentHashMap; 21 | import java.util.function.Consumer; 22 | 23 | import org.springframework.security.oauth2.core.user.OAuth2User; 24 | // end::imports[] 25 | 26 | /** 27 | * Example {@link Consumer} to perform JIT provisioning of an {@link OAuth2User}. 28 | * 29 | * @author Steve Riesenberg 30 | * @since 1.1 31 | */ 32 | // tag::class[] 33 | public final class UserRepositoryOAuth2UserHandler implements Consumer { 34 | 35 | private final UserRepository userRepository = new UserRepository(); 36 | 37 | @Override 38 | public void accept(OAuth2User user) { 39 | // Capture user in a local data store on first authentication 40 | if (this.userRepository.findByName(user.getName()) == null) { 41 | System.out.println("Saving first-time user: name=" + user.getName() + ", claims=" + user.getAttributes() + ", authorities=" + user.getAuthorities()); 42 | this.userRepository.save(user); 43 | } 44 | } 45 | 46 | static class UserRepository { 47 | 48 | private final Map userCache = new ConcurrentHashMap<>(); 49 | 50 | public OAuth2User findByName(String name) { 51 | return this.userCache.get(name); 52 | } 53 | 54 | public void save(OAuth2User oauth2User) { 55 | this.userCache.put(oauth2User.getName(), oauth2User); 56 | } 57 | 58 | } 59 | 60 | } 61 | // end::class[] 62 | -------------------------------------------------------------------------------- /git/hooks/prepare-forward-merge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'json' 3 | require 'net/http' 4 | require 'yaml' 5 | require 'logger' 6 | 7 | $main_branch = "1.3.x" 8 | 9 | $log = Logger.new(STDOUT) 10 | $log.level = Logger::WARN 11 | 12 | def get_fixed_issues() 13 | $log.debug "Searching for for forward merge" 14 | rev=`git rev-parse -q --verify MERGE_HEAD`.strip 15 | $log.debug "Found #{rev} from git rev-parse" 16 | return nil unless rev 17 | fixed = [] 18 | message = `git log -1 --pretty=%B #{rev}` 19 | message.each_line do |line| 20 | $log.debug "Checking #{line} for message" 21 | fixed << line.strip if /^(?:Fixes|Closes) gh-(\d+)/.match(line) 22 | end 23 | $log.debug "Found fixed issues #{fixed}" 24 | return fixed; 25 | end 26 | 27 | def rewrite_message(message_file, fixed) 28 | current_branch = `git rev-parse --abbrev-ref HEAD`.strip 29 | if current_branch == "main" 30 | current_branch = $main_branch 31 | end 32 | rewritten_message = "" 33 | message = File.read(message_file) 34 | message.each_line do |line| 35 | match = /^Merge.*branch\ '(.*)'(?:\ into\ (.*))?$/.match(line) 36 | if match 37 | from_branch = match[1] 38 | if from_branch.include? "/" 39 | from_branch = from_branch.partition("/").last 40 | end 41 | to_brach = match[2] 42 | $log.debug "Rewriting merge message" 43 | line = "Merge branch '#{from_branch}'" + (to_brach ? " into #{to_brach}\n" : "\n") 44 | end 45 | if fixed and line.start_with?("#") 46 | $log.debug "Adding fixed" 47 | rewritten_message << "\n" 48 | fixed.each do |fixes| 49 | rewritten_message << "#{fixes} in #{current_branch}\n" 50 | end 51 | fixed = nil 52 | end 53 | rewritten_message << line 54 | end 55 | return rewritten_message 56 | end 57 | 58 | $log.debug "Running prepare-forward-merge hook script" 59 | 60 | message_file=ARGV[0] 61 | message_type=ARGV[1] 62 | 63 | if message_type != "merge" 64 | $log.debug "Not a merge commit" 65 | exit 0; 66 | end 67 | 68 | $log.debug "Searching for for forward merge" 69 | fixed = get_fixed_issues() 70 | rewritten_message = rewrite_message(message_file, fixed) 71 | File.write(message_file, rewritten_message) 72 | -------------------------------------------------------------------------------- /.github/workflows/continuous-integration-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | schedule: 8 | - cron: '0 10 * * *' # Once per day at 10am UTC 9 | workflow_dispatch: 10 | 11 | jobs: 12 | build: 13 | name: Build 14 | uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@v1 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, windows-latest] 18 | jdk: [17] 19 | with: 20 | runs-on: ${{ matrix.os }} 21 | java-version: ${{ matrix.jdk }} 22 | distribution: temurin 23 | secrets: inherit 24 | test: 25 | name: Test Against Snapshots 26 | uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@v1 27 | with: 28 | test-args: --refresh-dependencies --stacktrace -PforceMavenRepositories=snapshot -PspringFrameworkVersion=6.1.+ -PspringSecurityVersion=6.3.+ 29 | secrets: inherit 30 | deploy-artifacts: 31 | name: Deploy Artifacts 32 | needs: [build, test] 33 | uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1 34 | with: 35 | should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }} 36 | secrets: inherit 37 | deploy-docs: 38 | name: Deploy Docs 39 | needs: [build, test] 40 | uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1 41 | with: 42 | should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }} 43 | secrets: inherit 44 | perform-release: 45 | name: Perform Release 46 | needs: [deploy-artifacts, deploy-docs] 47 | uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1 48 | with: 49 | should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }} 50 | project-version: ${{ needs.deploy-artifacts.outputs.project-version }} 51 | milestone-repo-url: https://repo.spring.io/artifactory/milestone 52 | release-repo-url: https://repo1.maven.org/maven2 53 | artifact-path: org/springframework/security/spring-security-oauth2-authorization-server 54 | slack-announcing-id: spring-authorization-server-announcing 55 | secrets: inherit -------------------------------------------------------------------------------- /docs/src/test/java/sample/util/RegisteredClients.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.util; 17 | 18 | import java.util.UUID; 19 | 20 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 21 | import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 22 | import org.springframework.security.oauth2.core.oidc.OidcScopes; 23 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 24 | import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; 25 | 26 | /** 27 | * @author Steve Riesenberg 28 | */ 29 | public class RegisteredClients { 30 | // @formatter:off 31 | public static RegisteredClient messagingClient() { 32 | return RegisteredClient.withId(UUID.randomUUID().toString()) 33 | .clientId("messaging-client") 34 | .clientSecret("{noop}secret") 35 | .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) 36 | .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) 37 | .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) 38 | .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) 39 | .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) 40 | .redirectUri("http://127.0.0.1:8080/authorized") 41 | .postLogoutRedirectUri("http://127.0.0.1:8080/index") 42 | .scope(OidcScopes.OPENID) 43 | .scope("message.read") 44 | .scope("message.write") 45 | .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) 46 | .build(); 47 | } 48 | // @formatter:on 49 | } 50 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.client; 17 | 18 | import org.springframework.lang.Nullable; 19 | 20 | /** 21 | * A repository for OAuth 2.0 {@link RegisteredClient}(s). 22 | * 23 | * @author Joe Grandja 24 | * @author Anoop Garlapati 25 | * @author Ovidiu Popa 26 | * @see RegisteredClient 27 | * @since 0.0.1 28 | */ 29 | public interface RegisteredClientRepository { 30 | 31 | /** 32 | * Saves the registered client. 33 | * 34 | *

35 | * IMPORTANT: Sensitive information should be encoded externally from the implementation, e.g. {@link RegisteredClient#getClientSecret()} 36 | * 37 | * @param registeredClient the {@link RegisteredClient} 38 | */ 39 | void save(RegisteredClient registeredClient); 40 | 41 | /** 42 | * Returns the registered client identified by the provided {@code id}, 43 | * or {@code null} if not found. 44 | * 45 | * @param id the registration identifier 46 | * @return the {@link RegisteredClient} if found, otherwise {@code null} 47 | */ 48 | @Nullable 49 | RegisteredClient findById(String id); 50 | 51 | /** 52 | * Returns the registered client identified by the provided {@code clientId}, 53 | * or {@code null} if not found. 54 | * 55 | * @param clientId the client identifier 56 | * @return the {@link RegisteredClient} if found, otherwise {@code null} 57 | */ 58 | @Nullable 59 | RegisteredClient findByClientId(String clientId); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContextHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.context; 17 | 18 | /** 19 | * A holder of the {@link AuthorizationServerContext} that associates it with the current thread using a {@code ThreadLocal}. 20 | * 21 | * @author Joe Grandja 22 | * @since 0.2.2 23 | * @see AuthorizationServerContext 24 | */ 25 | public final class AuthorizationServerContextHolder { 26 | private static final ThreadLocal holder = new ThreadLocal<>(); 27 | 28 | private AuthorizationServerContextHolder() { 29 | } 30 | 31 | /** 32 | * Returns the {@link AuthorizationServerContext} bound to the current thread. 33 | * 34 | * @return the {@link AuthorizationServerContext} 35 | */ 36 | public static AuthorizationServerContext getContext() { 37 | return holder.get(); 38 | } 39 | 40 | /** 41 | * Bind the given {@link AuthorizationServerContext} to the current thread. 42 | * 43 | * @param authorizationServerContext the {@link AuthorizationServerContext} 44 | */ 45 | public static void setContext(AuthorizationServerContext authorizationServerContext) { 46 | if (authorizationServerContext == null) { 47 | resetContext(); 48 | } else { 49 | holder.set(authorizationServerContext); 50 | } 51 | } 52 | 53 | /** 54 | * Reset the {@link AuthorizationServerContext} bound to the current thread. 55 | */ 56 | public static void resetContext() { 57 | holder.remove(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /docs/src/test/java/sample/test/SpringTestContextExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.test; 17 | 18 | import java.lang.reflect.Field; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import org.junit.jupiter.api.extension.AfterEachCallback; 23 | import org.junit.jupiter.api.extension.BeforeEachCallback; 24 | import org.junit.jupiter.api.extension.ExtensionContext; 25 | 26 | import org.springframework.security.test.context.TestSecurityContextHolder; 27 | 28 | /** 29 | * @author Rob Winch 30 | */ 31 | public class SpringTestContextExtension implements BeforeEachCallback, AfterEachCallback { 32 | 33 | @Override 34 | public void afterEach(ExtensionContext context) throws Exception { 35 | TestSecurityContextHolder.clearContext(); 36 | getContexts(context.getRequiredTestInstance()).forEach((springTestContext) -> springTestContext.close()); 37 | } 38 | 39 | @Override 40 | public void beforeEach(ExtensionContext context) throws Exception { 41 | Object testInstance = context.getRequiredTestInstance(); 42 | getContexts(testInstance).forEach((springTestContext) -> springTestContext.setTest(testInstance)); 43 | } 44 | 45 | private static List getContexts(Object test) throws IllegalAccessException { 46 | Field[] declaredFields = test.getClass().getDeclaredFields(); 47 | List result = new ArrayList<>(); 48 | for (Field field : declaredFields) { 49 | if (SpringTestContext.class.isAssignableFrom(field.getType())) { 50 | result.add((SpringTestContext) field.get(test)); 51 | } 52 | } 53 | return result; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /samples/messages-resource/src/main/java/sample/config/ResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.config; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.config.Customizer; 21 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 22 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 23 | import org.springframework.security.web.SecurityFilterChain; 24 | 25 | /** 26 | * @author Joe Grandja 27 | * @since 0.0.1 28 | */ 29 | @EnableWebSecurity 30 | @Configuration(proxyBeanMethods = false) 31 | public class ResourceServerConfig { 32 | 33 | /* 34 | NOTE: 35 | The `NimbusJwtDecoder` `@Bean` autoconfigured by Spring Boot will contain 36 | an `OAuth2TokenValidator` of type `X509CertificateThumbprintValidator`. 37 | This is the validator responsible for validating the `x5t#S256` claim (if available) 38 | in the `Jwt` against the SHA-256 Thumbprint of the supplied `X509Certificate`. 39 | */ 40 | 41 | // @formatter:off 42 | @Bean 43 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 44 | http 45 | .securityMatcher("/messages/**") 46 | .authorizeHttpRequests(authorize -> 47 | authorize.requestMatchers("/messages/**").hasAuthority("SCOPE_message.read") 48 | ) 49 | .oauth2ResourceServer(oauth2ResourceServer -> 50 | oauth2ResourceServer.jwt(Customizer.withDefaults()) 51 | ); 52 | return http.build(); 53 | } 54 | // @formatter:on 55 | 56 | } 57 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/jpa/repository/authorization/AuthorizationRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.jpa.repository.authorization; 17 | 18 | import java.util.Optional; 19 | 20 | import sample.jpa.entity.authorization.Authorization; 21 | 22 | import org.springframework.data.jpa.repository.JpaRepository; 23 | import org.springframework.data.jpa.repository.Query; 24 | import org.springframework.data.repository.query.Param; 25 | import org.springframework.stereotype.Repository; 26 | 27 | @Repository 28 | public interface AuthorizationRepository extends JpaRepository { 29 | Optional findByState(String state); 30 | Optional findByAuthorizationCodeValue(String authorizationCode); 31 | Optional findByAccessTokenValue(String accessToken); 32 | Optional findByRefreshTokenValue(String refreshToken); 33 | Optional findByOidcIdTokenValue(String idToken); 34 | Optional findByUserCodeValue(String userCode); 35 | Optional findByDeviceCodeValue(String deviceCode); 36 | @Query("select a from Authorization a where a.state = :token" + 37 | " or a.authorizationCodeValue = :token" + 38 | " or a.accessTokenValue = :token" + 39 | " or a.refreshTokenValue = :token" + 40 | " or a.oidcIdTokenValue = :token" + 41 | " or a.userCodeValue = :token" + 42 | " or a.deviceCodeValue = :token" 43 | ) 44 | Optional findByStateOrAuthorizationCodeValueOrAccessTokenValueOrRefreshTokenValueOrOidcIdTokenValueOrUserCodeValueOrDeviceCodeValue(@Param("token") String token); 45 | } 46 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/UnmodifiableMapDeserializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import java.io.IOException; 19 | import java.util.Collections; 20 | import java.util.LinkedHashMap; 21 | import java.util.Map; 22 | 23 | import com.fasterxml.jackson.core.JsonParser; 24 | import com.fasterxml.jackson.databind.DeserializationContext; 25 | import com.fasterxml.jackson.databind.JsonDeserializer; 26 | import com.fasterxml.jackson.databind.JsonNode; 27 | import com.fasterxml.jackson.databind.ObjectMapper; 28 | 29 | /** 30 | * A {@code JsonDeserializer} for {@link Collections#unmodifiableMap(Map)}. 31 | * 32 | * @author Joe Grandja 33 | * @since 0.1.2 34 | * @see Collections#unmodifiableMap(Map) 35 | * @see UnmodifiableMapMixin 36 | */ 37 | final class UnmodifiableMapDeserializer extends JsonDeserializer> { 38 | 39 | @Override 40 | public Map deserialize(JsonParser parser, DeserializationContext context) throws IOException { 41 | ObjectMapper mapper = (ObjectMapper) parser.getCodec(); 42 | JsonNode mapNode = mapper.readTree(parser); 43 | Map result = new LinkedHashMap<>(); 44 | if (mapNode != null && mapNode.isObject()) { 45 | Iterable> fields = mapNode::fields; 46 | for (Map.Entry field : fields) { 47 | result.put(field.getKey(), mapper.readValue(field.getValue().traverse(mapper), Object.class)); 48 | } 49 | } 50 | return Collections.unmodifiableMap(result); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2TokenExchangeCompositeAuthenticationTokenMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.security.oauth2.server.authorization.jackson2; 18 | 19 | import java.util.List; 20 | 21 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 22 | import com.fasterxml.jackson.annotation.JsonCreator; 23 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 24 | import com.fasterxml.jackson.annotation.JsonProperty; 25 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 26 | 27 | import org.springframework.security.core.Authentication; 28 | import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken; 29 | 30 | /** 31 | * This mixin class is used to serialize/deserialize {@link OAuth2TokenExchangeCompositeAuthenticationToken}. 32 | * 33 | * @author Steve Riesenberg 34 | * @since 1.3 35 | * @see OAuth2TokenExchangeCompositeAuthenticationToken 36 | */ 37 | @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) 38 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, 39 | isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE) 40 | @JsonIgnoreProperties(ignoreUnknown = true) 41 | abstract class OAuth2TokenExchangeCompositeAuthenticationTokenMixin { 42 | 43 | @JsonCreator 44 | OAuth2TokenExchangeCompositeAuthenticationTokenMixin(@JsonProperty("subject") Authentication subject, 45 | @JsonProperty("actors") List actors) { 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/registration/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.registration; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.config.Customizer; 21 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 22 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 23 | import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; 24 | import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer; 25 | import org.springframework.security.web.SecurityFilterChain; 26 | 27 | import static sample.registration.CustomClientMetadataConfig.configureCustomClientMetadataConverters; 28 | 29 | @Configuration 30 | @EnableWebSecurity 31 | public class SecurityConfig { 32 | 33 | @Bean 34 | public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { 35 | OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); 36 | http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) 37 | .oidc(oidc -> oidc.clientRegistrationEndpoint(clientRegistrationEndpoint -> { // <1> 38 | clientRegistrationEndpoint 39 | .authenticationProviders(configureCustomClientMetadataConverters()); // <2> 40 | })); 41 | http.oauth2ResourceServer(oauth2ResourceServer -> 42 | oauth2ResourceServer.jwt(Customizer.withDefaults())); 43 | 44 | return http.build(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/Context.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.context; 17 | 18 | import org.springframework.lang.Nullable; 19 | import org.springframework.util.Assert; 20 | 21 | /** 22 | * A facility for holding information associated to a specific context. 23 | * 24 | * @author Joe Grandja 25 | * @since 0.1.0 26 | */ 27 | public interface Context { 28 | 29 | /** 30 | * Returns the value of the attribute associated to the key. 31 | * 32 | * @param key the key for the attribute 33 | * @param the type of the value for the attribute 34 | * @return the value of the attribute associated to the key, or {@code null} if not available 35 | */ 36 | @Nullable 37 | V get(Object key); 38 | 39 | /** 40 | * Returns the value of the attribute associated to the key. 41 | * 42 | * @param key the key for the attribute 43 | * @param the type of the value for the attribute 44 | * @return the value of the attribute associated to the key, or {@code null} if not available or not of the specified type 45 | */ 46 | @Nullable 47 | default V get(Class key) { 48 | Assert.notNull(key, "key cannot be null"); 49 | V value = get((Object) key); 50 | return key.isInstance(value) ? value : null; 51 | } 52 | 53 | /** 54 | * Returns {@code true} if an attribute associated to the key exists, {@code false} otherwise. 55 | * 56 | * @param key the key for the attribute 57 | * @return {@code true} if an attribute associated to the key exists, {@code false} otherwise 58 | */ 59 | boolean hasKey(Object key); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.token; 17 | 18 | import org.springframework.lang.Nullable; 19 | import org.springframework.security.oauth2.core.ClaimAccessor; 20 | import org.springframework.security.oauth2.core.OAuth2Token; 21 | import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; 22 | 23 | /** 24 | * Implementations of this interface are responsible for generating an {@link OAuth2Token} 25 | * using the attributes contained in the {@link OAuth2TokenContext}. 26 | * 27 | * @author Joe Grandja 28 | * @since 0.2.3 29 | * @see OAuth2Token 30 | * @see OAuth2TokenContext 31 | * @see OAuth2TokenClaimsSet 32 | * @see ClaimAccessor 33 | * @param the type of the OAuth 2.0 Token 34 | */ 35 | @FunctionalInterface 36 | public interface OAuth2TokenGenerator { 37 | 38 | /** 39 | * Generate an OAuth 2.0 Token using the attributes contained in the {@link OAuth2TokenContext}, 40 | * or return {@code null} if the {@link OAuth2TokenContext#getTokenType()} is not supported. 41 | * 42 | *

43 | * If the returned {@link OAuth2Token} has a set of claims, it should implement {@link ClaimAccessor} 44 | * in order for it to be stored with the {@link OAuth2Authorization}. 45 | * 46 | * @param context the context containing the OAuth 2.0 Token attributes 47 | * @return an {@link OAuth2Token} or {@code null} if the {@link OAuth2TokenContext#getTokenType()} is not supported 48 | */ 49 | @Nullable 50 | T generate(OAuth2TokenContext context); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/test/SpringTestContextExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.test; 17 | 18 | import java.lang.reflect.Field; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import org.junit.jupiter.api.extension.AfterEachCallback; 23 | import org.junit.jupiter.api.extension.BeforeEachCallback; 24 | import org.junit.jupiter.api.extension.ExtensionContext; 25 | 26 | import org.springframework.security.test.context.TestSecurityContextHolder; 27 | 28 | public class SpringTestContextExtension implements BeforeEachCallback, AfterEachCallback { 29 | 30 | @Override 31 | public void beforeEach(ExtensionContext context) throws Exception { 32 | Object testInstance = context.getRequiredTestInstance(); 33 | getContexts(testInstance).forEach((springTestContext) -> springTestContext.setTest(testInstance)); 34 | } 35 | 36 | @Override 37 | public void afterEach(ExtensionContext context) throws Exception { 38 | TestSecurityContextHolder.clearContext(); 39 | Object testInstance = context.getRequiredTestInstance(); 40 | getContexts(testInstance).forEach(SpringTestContext::close); 41 | } 42 | 43 | private static List getContexts(Object test) throws IllegalAccessException { 44 | Field[] declaredFields = test.getClass().getDeclaredFields(); 45 | List result = new ArrayList<>(); 46 | for (Field field : declaredFields) { 47 | if (SpringTestContext.class.isAssignableFrom(field.getType())) { 48 | result.add((SpringTestContext) field.get(test)); 49 | } 50 | } 51 | return result; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/pkce/ClientConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.pkce; 17 | 18 | import java.util.UUID; 19 | 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 23 | import org.springframework.security.oauth2.core.ClientAuthenticationMethod; 24 | import org.springframework.security.oauth2.core.oidc.OidcScopes; 25 | import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository; 26 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 27 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; 28 | import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; 29 | 30 | @Configuration 31 | public class ClientConfig { 32 | 33 | // tag::client[] 34 | @Bean 35 | public RegisteredClientRepository registeredClientRepository() { 36 | // @formatter:off 37 | RegisteredClient publicClient = RegisteredClient.withId(UUID.randomUUID().toString()) 38 | .clientId("public-client") 39 | .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) 40 | .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) 41 | .redirectUri("http://127.0.0.1:4200") 42 | .scope(OidcScopes.OPENID) 43 | .scope(OidcScopes.PROFILE) 44 | .clientSettings(ClientSettings.builder() 45 | .requireAuthorizationConsent(true) 46 | .requireProofKey(true) 47 | .build() 48 | ) 49 | .build(); 50 | // @formatter:on 51 | 52 | return new InMemoryRegisteredClientRepository(publicClient); 53 | } 54 | // end::client[] 55 | 56 | } 57 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization; 17 | 18 | import org.springframework.lang.Nullable; 19 | import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 20 | 21 | import java.security.Principal; 22 | 23 | /** 24 | * Implementations of this interface are responsible for the management 25 | * of {@link OAuth2AuthorizationConsent OAuth 2.0 Authorization Consent(s)}. 26 | * 27 | * @author Daniel Garnier-Moiroux 28 | * @since 0.1.2 29 | * @see OAuth2AuthorizationConsent 30 | */ 31 | public interface OAuth2AuthorizationConsentService { 32 | 33 | /** 34 | * Saves the {@link OAuth2AuthorizationConsent}. 35 | * 36 | * @param authorizationConsent the {@link OAuth2AuthorizationConsent} 37 | */ 38 | void save(OAuth2AuthorizationConsent authorizationConsent); 39 | 40 | /** 41 | * Removes the {@link OAuth2AuthorizationConsent}. 42 | * 43 | * @param authorizationConsent the {@link OAuth2AuthorizationConsent} 44 | */ 45 | void remove(OAuth2AuthorizationConsent authorizationConsent); 46 | 47 | /** 48 | * Returns the {@link OAuth2AuthorizationConsent} identified by the provided 49 | * {@code registeredClientId} and {@code principalName}, or {@code null} if not found. 50 | * 51 | * @param registeredClientId the identifier for the {@link RegisteredClient} 52 | * @param principalName the name of the {@link Principal} 53 | * @return the {@link OAuth2AuthorizationConsent} if found, otherwise {@code null} 54 | */ 55 | @Nullable 56 | OAuth2AuthorizationConsent findById(String registeredClientId, String principalName); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JsonNodeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.jackson2; 17 | 18 | import java.util.Map; 19 | import java.util.Set; 20 | 21 | import com.fasterxml.jackson.core.type.TypeReference; 22 | import com.fasterxml.jackson.databind.JsonNode; 23 | import com.fasterxml.jackson.databind.ObjectMapper; 24 | 25 | /** 26 | * Utility class for {@code JsonNode}. 27 | * 28 | * @author Joe Grandja 29 | * @since 0.1.2 30 | */ 31 | abstract class JsonNodeUtils { 32 | 33 | static final TypeReference> STRING_SET = new TypeReference>() { 34 | }; 35 | 36 | static final TypeReference> STRING_OBJECT_MAP = new TypeReference>() { 37 | }; 38 | 39 | static String findStringValue(JsonNode jsonNode, String fieldName) { 40 | if (jsonNode == null) { 41 | return null; 42 | } 43 | JsonNode value = jsonNode.findValue(fieldName); 44 | return (value != null && value.isTextual()) ? value.asText() : null; 45 | } 46 | 47 | static T findValue(JsonNode jsonNode, String fieldName, TypeReference valueTypeReference, 48 | ObjectMapper mapper) { 49 | if (jsonNode == null) { 50 | return null; 51 | } 52 | JsonNode value = jsonNode.findValue(fieldName); 53 | return (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null; 54 | } 55 | 56 | static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) { 57 | if (jsonNode == null) { 58 | return null; 59 | } 60 | JsonNode value = jsonNode.findValue(fieldName); 61 | return (value != null && value.isObject()) ? value : null; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/DemoAuthorizationServerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample; 17 | 18 | import java.util.Arrays; 19 | 20 | import org.thymeleaf.expression.Lists; 21 | import sample.web.AuthorizationConsentController; 22 | 23 | import org.springframework.aot.hint.MemberCategory; 24 | import org.springframework.aot.hint.RuntimeHints; 25 | import org.springframework.aot.hint.RuntimeHintsRegistrar; 26 | import org.springframework.aot.hint.TypeReference; 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.context.annotation.ImportRuntimeHints; 30 | 31 | /** 32 | * @author Joe Grandja 33 | * @author Josh Long 34 | * @since 1.1 35 | */ 36 | @SpringBootApplication 37 | @ImportRuntimeHints(DemoAuthorizationServerApplication.DemoAuthorizationServerApplicationRuntimeHintsRegistrar.class) 38 | public class DemoAuthorizationServerApplication { 39 | 40 | static class DemoAuthorizationServerApplicationRuntimeHintsRegistrar implements RuntimeHintsRegistrar { 41 | 42 | @Override 43 | public void registerHints(RuntimeHints hints, ClassLoader classLoader) { 44 | // Thymeleaf 45 | hints.reflection().registerTypes( 46 | Arrays.asList( 47 | TypeReference.of(AuthorizationConsentController.ScopeWithDescription.class), 48 | TypeReference.of(Lists.class) 49 | ), builder -> 50 | builder.withMembers(MemberCategory.DECLARED_FIELDS, 51 | MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS) 52 | ); 53 | } 54 | 55 | } 56 | 57 | public static void main(String[] args) { 58 | SpringApplication.run(DemoAuthorizationServerApplication.class, args); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization; 17 | 18 | import org.springframework.lang.Nullable; 19 | 20 | /** 21 | * Implementations of this interface are responsible for the management 22 | * of {@link OAuth2Authorization OAuth 2.0 Authorization(s)}. 23 | * 24 | * @author Joe Grandja 25 | * @since 0.0.1 26 | * @see OAuth2Authorization 27 | * @see OAuth2TokenType 28 | */ 29 | public interface OAuth2AuthorizationService { 30 | 31 | /** 32 | * Saves the {@link OAuth2Authorization}. 33 | * 34 | * @param authorization the {@link OAuth2Authorization} 35 | */ 36 | void save(OAuth2Authorization authorization); 37 | 38 | /** 39 | * Removes the {@link OAuth2Authorization}. 40 | * 41 | * @param authorization the {@link OAuth2Authorization} 42 | */ 43 | void remove(OAuth2Authorization authorization); 44 | 45 | /** 46 | * Returns the {@link OAuth2Authorization} identified by the provided {@code id}, 47 | * or {@code null} if not found. 48 | * 49 | * @param id the authorization identifier 50 | * @return the {@link OAuth2Authorization} if found, otherwise {@code null} 51 | */ 52 | @Nullable 53 | OAuth2Authorization findById(String id); 54 | 55 | /** 56 | * Returns the {@link OAuth2Authorization} containing the provided {@code token}, 57 | * or {@code null} if not found. 58 | * 59 | * @param token the token credential 60 | * @param tokenType the {@link OAuth2TokenType token type} 61 | * @return the {@link OAuth2Authorization} if found, otherwise {@code null} 62 | */ 63 | @Nullable 64 | OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType); 65 | 66 | } 67 | -------------------------------------------------------------------------------- /docs/spring-authorization-server-docs.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "io.spring.convention.docs" 3 | id "io.spring.antora.generate-antora-yml" version "0.0.1" 4 | id "org.antora" version "1.0.0" 5 | id "java" 6 | } 7 | 8 | group = project.rootProject.group 9 | version = project.rootProject.version 10 | 11 | java { 12 | sourceCompatibility = JavaVersion.VERSION_17 13 | } 14 | 15 | antora { 16 | playbook = "cached-antora-playbook.yml" 17 | playbookProvider { 18 | repository = "spring-projects/spring-authorization-server" 19 | branch = "docs-build" 20 | path = "lib/antora/templates/per-branch-antora-playbook.yml" 21 | checkLocalBranch = true 22 | } 23 | options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true] 24 | } 25 | 26 | tasks.named("generateAntoraYml") { 27 | asciidocAttributes = project.provider( { generateAttributes() } ) 28 | } 29 | 30 | 31 | def generateAttributes() { 32 | return [ 33 | "spring-authorization-server-version": project.version 34 | ] 35 | } 36 | 37 | docsZip { 38 | from (api) { 39 | into "api" 40 | } 41 | from(asciidoctor) { 42 | into "reference/html" 43 | } 44 | } 45 | 46 | repositories { 47 | mavenCentral() 48 | maven { url "https://repo.spring.io/milestone" } 49 | maven { url "https://repo.spring.io/snapshot" } 50 | } 51 | 52 | dependencies { 53 | implementation(platform("org.springframework.boot:spring-boot-dependencies:3.2.2")) { 54 | exclude group: "org.springframework.security", module: "spring-security-oauth2-authorization-server" 55 | } 56 | implementation platform("org.springframework.security:spring-security-bom:6.3.0-RC1") 57 | implementation "org.springframework.boot:spring-boot-starter-web" 58 | implementation "org.springframework.boot:spring-boot-starter-thymeleaf" 59 | implementation "org.springframework.boot:spring-boot-starter-security" 60 | implementation "org.springframework.boot:spring-boot-starter-oauth2-client" 61 | implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server" 62 | implementation "org.springframework.boot:spring-boot-starter-data-jpa" 63 | implementation "org.springframework:spring-webflux" 64 | implementation project(":spring-security-oauth2-authorization-server") 65 | runtimeOnly "com.h2database:h2" 66 | testImplementation "org.springframework.boot:spring-boot-starter-test" 67 | testImplementation "org.springframework.security:spring-security-test" 68 | } 69 | 70 | tasks.named("test") { 71 | useJUnitPlatform() 72 | } 73 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.authentication; 17 | 18 | import java.util.Map; 19 | 20 | import org.springframework.lang.Nullable; 21 | import org.springframework.security.core.Authentication; 22 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 23 | import org.springframework.util.Assert; 24 | 25 | /** 26 | * An {@link Authentication} implementation for the Device Access Token Request 27 | * used in the OAuth 2.0 Device Authorization Grant. 28 | * 29 | * @author Steve Riesenberg 30 | * @since 1.1 31 | * @see OAuth2AuthorizationGrantAuthenticationToken 32 | * @see OAuth2DeviceCodeAuthenticationProvider 33 | */ 34 | public class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { 35 | 36 | private final String deviceCode; 37 | 38 | /** 39 | * Constructs an {@code OAuth2DeviceCodeAuthenticationToken} using the provided parameters. 40 | * 41 | * @param deviceCode the device code 42 | * @param clientPrincipal the authenticated client principal 43 | * @param additionalParameters the additional parameters 44 | */ 45 | public OAuth2DeviceCodeAuthenticationToken(String deviceCode, Authentication clientPrincipal, 46 | @Nullable Map additionalParameters) { 47 | super(AuthorizationGrantType.DEVICE_CODE, clientPrincipal, additionalParameters); 48 | Assert.hasText(deviceCode, "deviceCode cannot be empty"); 49 | this.deviceCode = deviceCode; 50 | } 51 | 52 | /** 53 | * Returns the device code. 54 | * 55 | * @return the device code 56 | */ 57 | public String getDeviceCode() { 58 | return this.deviceCode; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Spring Authorization Server sample 7 | 8 | 9 | 10 | 11 |

12 | 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /samples/demo-client/src/main/java/sample/web/JwkSetController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import java.security.KeyStore; 19 | import java.security.cert.Certificate; 20 | import java.security.interfaces.RSAPublicKey; 21 | import java.util.Collections; 22 | import java.util.Map; 23 | import java.util.UUID; 24 | 25 | import com.nimbusds.jose.jwk.JWKSet; 26 | import com.nimbusds.jose.jwk.KeyUse; 27 | import com.nimbusds.jose.jwk.RSAKey; 28 | import com.nimbusds.jose.util.Base64; 29 | 30 | import org.springframework.boot.ssl.SslBundle; 31 | import org.springframework.boot.ssl.SslBundles; 32 | import org.springframework.web.bind.annotation.GetMapping; 33 | import org.springframework.web.bind.annotation.RestController; 34 | 35 | /** 36 | * @author Joe Grandja 37 | * @since 1.3 38 | */ 39 | @RestController 40 | public class JwkSetController { 41 | private final JWKSet jwkSet; 42 | 43 | public JwkSetController(SslBundles sslBundles) throws Exception { 44 | this.jwkSet = initJwkSet(sslBundles); 45 | } 46 | 47 | @GetMapping("/jwks") 48 | public Map getJwkSet() { 49 | return this.jwkSet.toJSONObject(); 50 | } 51 | 52 | private static JWKSet initJwkSet(SslBundles sslBundles) throws Exception { 53 | SslBundle sslBundle = sslBundles.getBundle("self-signed-demo-client"); 54 | KeyStore keyStore = sslBundle.getStores().getKeyStore(); 55 | String alias = sslBundle.getKey().getAlias(); 56 | 57 | Certificate certificate = keyStore.getCertificate(alias); 58 | 59 | RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) certificate.getPublicKey()) 60 | .keyUse(KeyUse.SIGNATURE) 61 | .keyID(UUID.randomUUID().toString()) 62 | .x509CertChain(Collections.singletonList(Base64.encode(certificate.getEncoded()))) 63 | .build(); 64 | 65 | return new JWKSet(rsaKey); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenExchangeActor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.security.oauth2.server.authorization.authentication; 18 | 19 | import java.util.Collections; 20 | import java.util.Map; 21 | import java.util.Objects; 22 | 23 | import org.springframework.security.oauth2.core.ClaimAccessor; 24 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames; 25 | import org.springframework.util.Assert; 26 | 27 | /** 28 | * A {@link ClaimAccessor} used for the OAuth 2.0 Token Exchange Grant to represent an actor in a 29 | * {@link OAuth2TokenExchangeCompositeAuthenticationToken} (e.g. the "delegation" use case). 30 | * 31 | * @author Steve Riesenberg 32 | * @since 1.3 33 | * @see OAuth2TokenExchangeCompositeAuthenticationToken 34 | */ 35 | public final class OAuth2TokenExchangeActor implements ClaimAccessor { 36 | 37 | private final Map claims; 38 | 39 | public OAuth2TokenExchangeActor(Map claims) { 40 | Assert.notNull(claims, "claims cannot be null"); 41 | this.claims = Collections.unmodifiableMap(claims); 42 | } 43 | 44 | @Override 45 | public Map getClaims() { 46 | return this.claims; 47 | } 48 | 49 | public String getIssuer() { 50 | return getClaimAsString(OAuth2TokenClaimNames.ISS); 51 | } 52 | 53 | public String getSubject() { 54 | return getClaimAsString(OAuth2TokenClaimNames.SUB); 55 | } 56 | 57 | @Override 58 | public boolean equals(Object obj) { 59 | if (!(obj instanceof OAuth2TokenExchangeActor other)) { 60 | return false; 61 | } 62 | return Objects.equals(this.claims, other.claims); 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return Objects.hash(this.claims); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.adoc: -------------------------------------------------------------------------------- 1 | = Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open 4 | and welcoming community, we pledge to respect all people who contribute through reporting 5 | issues, posting feature requests, updating documentation, submitting pull requests or 6 | patches, and other activities. 7 | 8 | We are committed to making participation in this project a harassment-free experience for 9 | everyone, regardless of level of experience, gender, gender identity and expression, 10 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, 11 | religion, or nationality. 12 | 13 | Examples of unacceptable behavior by participants include: 14 | 15 | * The use of sexualized language or imagery 16 | * Personal attacks 17 | * Trolling or insulting/derogatory comments 18 | * Public or private harassment 19 | * Publishing other's private information, such as physical or electronic addresses, 20 | without explicit permission 21 | * Other unethical or unprofessional conduct 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 24 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 25 | Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors 26 | that they deem inappropriate, threatening, offensive, or harmful. 27 | 28 | By adopting this Code of Conduct, project maintainers commit themselves to fairly and 29 | consistently applying these principles to every aspect of managing this project. Project 30 | maintainers who do not follow or enforce the Code of Conduct may be permanently removed 31 | from the project team. 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an 34 | individual is representing the project or its community. 35 | 36 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 37 | contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will 38 | be reviewed and investigated and will result in a response that is deemed necessary and 39 | appropriate to the circumstances. Maintainers are obligated to maintain confidentiality 40 | with regard to the reporter of an incident. 41 | 42 | This Code of Conduct is adapted from the 43 | https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at 44 | https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/] 45 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DefaultOAuth2TokenContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.token; 17 | 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import org.springframework.lang.Nullable; 23 | import org.springframework.util.Assert; 24 | 25 | /** 26 | * Default implementation of {@link OAuth2TokenContext}. 27 | * 28 | * @author Joe Grandja 29 | * @since 0.2.3 30 | * @see OAuth2TokenContext 31 | */ 32 | public final class DefaultOAuth2TokenContext implements OAuth2TokenContext { 33 | private final Map context; 34 | 35 | private DefaultOAuth2TokenContext(Map context) { 36 | this.context = Collections.unmodifiableMap(new HashMap<>(context)); 37 | } 38 | 39 | @SuppressWarnings("unchecked") 40 | @Nullable 41 | @Override 42 | public V get(Object key) { 43 | return hasKey(key) ? (V) this.context.get(key) : null; 44 | } 45 | 46 | @Override 47 | public boolean hasKey(Object key) { 48 | Assert.notNull(key, "key cannot be null"); 49 | return this.context.containsKey(key); 50 | } 51 | 52 | /** 53 | * Returns a new {@link Builder}. 54 | * 55 | * @return the {@link Builder} 56 | */ 57 | public static Builder builder() { 58 | return new Builder(); 59 | } 60 | 61 | /** 62 | * A builder for {@link DefaultOAuth2TokenContext}. 63 | */ 64 | public static final class Builder extends AbstractBuilder { 65 | 66 | private Builder() { 67 | } 68 | 69 | /** 70 | * Builds a new {@link DefaultOAuth2TokenContext}. 71 | * 72 | * @return the {@link DefaultOAuth2TokenContext} 73 | */ 74 | public DefaultOAuth2TokenContext build() { 75 | return new DefaultOAuth2TokenContext(getContext()); 76 | } 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/HttpMessageConverters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.http.converter; 17 | 18 | import org.springframework.http.converter.GenericHttpMessageConverter; 19 | import org.springframework.http.converter.HttpMessageConverter; 20 | import org.springframework.http.converter.json.GsonHttpMessageConverter; 21 | import org.springframework.http.converter.json.JsonbHttpMessageConverter; 22 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 23 | import org.springframework.util.ClassUtils; 24 | 25 | /** 26 | * Utility methods for {@link HttpMessageConverter}'s. 27 | * 28 | * @author Joe Grandja 29 | * @author luamas 30 | * @since 0.1.1 31 | */ 32 | final class HttpMessageConverters { 33 | 34 | private static final boolean jackson2Present; 35 | 36 | private static final boolean gsonPresent; 37 | 38 | private static final boolean jsonbPresent; 39 | 40 | static { 41 | ClassLoader classLoader = HttpMessageConverters.class.getClassLoader(); 42 | jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) 43 | && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); 44 | gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); 45 | jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader); 46 | } 47 | 48 | private HttpMessageConverters() { 49 | } 50 | 51 | static GenericHttpMessageConverter getJsonMessageConverter() { 52 | if (jackson2Present) { 53 | return new MappingJackson2HttpMessageConverter(); 54 | } 55 | if (gsonPresent) { 56 | return new GsonHttpMessageConverter(); 57 | } 58 | if (jsonbPresent) { 59 | return new JsonbHttpMessageConverter(); 60 | } 61 | return null; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/HttpMessageConverters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.oidc.http.converter; 17 | 18 | import org.springframework.http.converter.GenericHttpMessageConverter; 19 | import org.springframework.http.converter.HttpMessageConverter; 20 | import org.springframework.http.converter.json.GsonHttpMessageConverter; 21 | import org.springframework.http.converter.json.JsonbHttpMessageConverter; 22 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 23 | import org.springframework.util.ClassUtils; 24 | 25 | /** 26 | * Utility methods for {@link HttpMessageConverter}'s. 27 | * 28 | * @author Joe Grandja 29 | * @author luamas 30 | * @since 0.1.0 31 | */ 32 | final class HttpMessageConverters { 33 | 34 | private static final boolean jackson2Present; 35 | 36 | private static final boolean gsonPresent; 37 | 38 | private static final boolean jsonbPresent; 39 | 40 | static { 41 | ClassLoader classLoader = HttpMessageConverters.class.getClassLoader(); 42 | jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) 43 | && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); 44 | gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); 45 | jsonbPresent = ClassUtils.isPresent("jakarta.json.bind.Jsonb", classLoader); 46 | } 47 | 48 | private HttpMessageConverters() { 49 | } 50 | 51 | static GenericHttpMessageConverter getJsonMessageConverter() { 52 | if (jackson2Present) { 53 | return new MappingJackson2HttpMessageConverter(); 54 | } 55 | if (gsonPresent) { 56 | return new GsonHttpMessageConverter(); 57 | } 58 | if (jsonbPresent) { 59 | return new JsonbHttpMessageConverter(); 60 | } 61 | return null; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /samples/users-resource/src/main/java/sample/web/UserController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.web; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import java.util.Objects; 22 | 23 | import org.springframework.beans.factory.annotation.Value; 24 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 25 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; 26 | import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; 27 | import org.springframework.security.oauth2.jwt.Jwt; 28 | import org.springframework.web.bind.annotation.GetMapping; 29 | import org.springframework.web.bind.annotation.RestController; 30 | import org.springframework.web.client.RestClient; 31 | 32 | /** 33 | * @author Steve Riesenberg 34 | * @since 1.3 35 | */ 36 | @RestController 37 | public class UserController { 38 | 39 | private final RestClient restClient; 40 | 41 | public UserController(@Value("${messages.base-uri}") String baseUrl) { 42 | this.restClient = RestClient.builder() 43 | .baseUrl(baseUrl) 44 | .build(); 45 | } 46 | 47 | @GetMapping("/user/messages") 48 | public List getMessages(@AuthenticationPrincipal Jwt jwt, 49 | @RegisteredOAuth2AuthorizedClient("messaging-client-token-exchange") 50 | OAuth2AuthorizedClient authorizedClient) { 51 | 52 | // @formatter:off 53 | String[] messages = Objects.requireNonNull( 54 | this.restClient.get() 55 | .uri("/messages") 56 | .headers((headers) -> headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue())) 57 | .retrieve() 58 | .body(String[].class) 59 | ); 60 | // @formatter:on 61 | 62 | List userMessages = new ArrayList<>(Arrays.asList(messages)); 63 | userMessages.add("%s has %d unread messages".formatted(jwt.getSubject(), messages.length)); 64 | 65 | return userMessages; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.authentication; 17 | 18 | import java.util.Collections; 19 | import java.util.HashSet; 20 | import java.util.Map; 21 | import java.util.Set; 22 | 23 | import org.springframework.lang.Nullable; 24 | import org.springframework.security.core.Authentication; 25 | import org.springframework.security.oauth2.core.AuthorizationGrantType; 26 | 27 | /** 28 | * An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials Grant. 29 | * 30 | * @author Alexey Nesterov 31 | * @since 0.0.1 32 | * @see OAuth2AuthorizationGrantAuthenticationToken 33 | * @see OAuth2ClientCredentialsAuthenticationProvider 34 | */ 35 | public class OAuth2ClientCredentialsAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { 36 | private final Set scopes; 37 | 38 | /** 39 | * Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters. 40 | * 41 | * @param clientPrincipal the authenticated client principal 42 | * @param scopes the requested scope(s) 43 | * @param additionalParameters the additional parameters 44 | */ 45 | public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, 46 | @Nullable Set scopes, @Nullable Map additionalParameters) { 47 | super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, additionalParameters); 48 | this.scopes = Collections.unmodifiableSet( 49 | scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); 50 | } 51 | 52 | /** 53 | * Returns the requested scope(s). 54 | * 55 | * @return the requested scope(s), or an empty {@code Set} if not available 56 | */ 57 | public Set getScopes() { 58 | return this.scopes; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization; 17 | 18 | import java.io.Serializable; 19 | 20 | import org.springframework.security.oauth2.server.authorization.util.SpringAuthorizationServerVersion; 21 | import org.springframework.util.Assert; 22 | 23 | /** 24 | * Standard token types defined in the OAuth Token Type Hints Registry. 25 | * 26 | * @author Joe Grandja 27 | * @since 0.0.1 28 | * @see 4.1.2 OAuth Token Type Hints Registry 29 | */ 30 | public final class OAuth2TokenType implements Serializable { 31 | private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; 32 | public static final OAuth2TokenType ACCESS_TOKEN = new OAuth2TokenType("access_token"); 33 | public static final OAuth2TokenType REFRESH_TOKEN = new OAuth2TokenType("refresh_token"); 34 | private final String value; 35 | 36 | /** 37 | * Constructs an {@code OAuth2TokenType} using the provided value. 38 | * 39 | * @param value the value of the token type 40 | */ 41 | public OAuth2TokenType(String value) { 42 | Assert.hasText(value, "value cannot be empty"); 43 | this.value = value; 44 | } 45 | 46 | /** 47 | * Returns the value of the token type. 48 | * 49 | * @return the value of the token type 50 | */ 51 | public String getValue() { 52 | return this.value; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object obj) { 57 | if (this == obj) { 58 | return true; 59 | } 60 | if (obj == null || this.getClass() != obj.getClass()) { 61 | return false; 62 | } 63 | OAuth2TokenType that = (OAuth2TokenType) obj; 64 | return getValue().equals(that.getValue()); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return getValue().hashCode(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /samples/demo-authorizationserver/src/main/java/sample/jose/Jwks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.jose; 17 | 18 | import java.security.KeyPair; 19 | import java.security.interfaces.ECPrivateKey; 20 | import java.security.interfaces.ECPublicKey; 21 | import java.security.interfaces.RSAPrivateKey; 22 | import java.security.interfaces.RSAPublicKey; 23 | import java.util.UUID; 24 | 25 | import javax.crypto.SecretKey; 26 | 27 | import com.nimbusds.jose.jwk.Curve; 28 | import com.nimbusds.jose.jwk.ECKey; 29 | import com.nimbusds.jose.jwk.OctetSequenceKey; 30 | import com.nimbusds.jose.jwk.RSAKey; 31 | 32 | /** 33 | * @author Joe Grandja 34 | * @since 1.1 35 | */ 36 | public final class Jwks { 37 | 38 | private Jwks() { 39 | } 40 | 41 | public static RSAKey generateRsa() { 42 | KeyPair keyPair = KeyGeneratorUtils.generateRsaKey(); 43 | RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); 44 | RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); 45 | // @formatter:off 46 | return new RSAKey.Builder(publicKey) 47 | .privateKey(privateKey) 48 | .keyID(UUID.randomUUID().toString()) 49 | .build(); 50 | // @formatter:on 51 | } 52 | 53 | public static ECKey generateEc() { 54 | KeyPair keyPair = KeyGeneratorUtils.generateEcKey(); 55 | ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic(); 56 | ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate(); 57 | Curve curve = Curve.forECParameterSpec(publicKey.getParams()); 58 | // @formatter:off 59 | return new ECKey.Builder(curve, publicKey) 60 | .privateKey(privateKey) 61 | .keyID(UUID.randomUUID().toString()) 62 | .build(); 63 | // @formatter:on 64 | } 65 | 66 | public static OctetSequenceKey generateSecret() { 67 | SecretKey secretKey = KeyGeneratorUtils.generateSecretKey(); 68 | // @formatter:off 69 | return new OctetSequenceKey.Builder(secretKey) 70 | .keyID(UUID.randomUUID().toString()) 71 | .build(); 72 | // @formatter:on 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimNames.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.token; 17 | 18 | import org.springframework.security.oauth2.core.OAuth2Token; 19 | 20 | /** 21 | * The names of the "claims" that may be contained in an {@link OAuth2TokenClaimsSet} 22 | * and are associated to an {@link OAuth2Token}. 23 | * 24 | * @author Joe Grandja 25 | * @since 0.2.3 26 | * @see OAuth2TokenClaimAccessor 27 | * @see OAuth2TokenClaimsSet 28 | * @see OAuth2Token 29 | */ 30 | public final class OAuth2TokenClaimNames { 31 | 32 | /** 33 | * {@code iss} - the Issuer claim identifies the principal that issued the OAuth 2.0 Token 34 | */ 35 | public static final String ISS = "iss"; 36 | 37 | /** 38 | * {@code sub} - the Subject claim identifies the principal that is the subject of the OAuth 2.0 Token 39 | */ 40 | public static final String SUB = "sub"; 41 | 42 | /** 43 | * {@code aud} - the Audience claim identifies the recipient(s) that the OAuth 2.0 Token is intended for 44 | */ 45 | public static final String AUD = "aud"; 46 | 47 | /** 48 | * {@code exp} - the Expiration time claim identifies the expiration time on or after 49 | * which the OAuth 2.0 Token MUST NOT be accepted for processing 50 | */ 51 | public static final String EXP = "exp"; 52 | 53 | /** 54 | * {@code nbf} - the Not Before claim identifies the time before which the OAuth 2.0 Token 55 | * MUST NOT be accepted for processing 56 | */ 57 | public static final String NBF = "nbf"; 58 | 59 | /** 60 | * {@code iat} - The Issued at claim identifies the time at which the OAuth 2.0 Token was issued 61 | */ 62 | public static final String IAT = "iat"; 63 | 64 | /** 65 | * {@code jti} - The ID claim provides a unique identifier for the OAuth 2.0 Token 66 | */ 67 | public static final String JTI = "jti"; 68 | 69 | private OAuth2TokenClaimNames() { 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2024 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.context; 17 | 18 | import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings; 19 | 20 | /** 21 | * A context that holds information of the Authorization Server runtime environment. 22 | * 23 | * @author Joe Grandja 24 | * @since 0.2.2 25 | * @see AuthorizationServerSettings 26 | * @see AuthorizationServerContextHolder 27 | */ 28 | public interface AuthorizationServerContext { 29 | 30 | /** 31 | * Returns {@link AuthorizationServerSettings#getIssuer()} if available, otherwise, 32 | * resolves the issuer identifier from the "current" request. 33 | * 34 | *

35 | * The issuer identifier may contain a path component to support multiple issuers per host in a multi-tenant hosting configuration. 36 | * 37 | *

38 | * For example: 39 | *

    40 | *
  • {@code https://example.com/issuer1/oauth2/token} — resolves the issuer to {@code https://example.com/issuer1}
  • 41 | *
  • {@code https://example.com/issuer2/oauth2/token} — resolves the issuer to {@code https://example.com/issuer2}
  • 42 | *
  • {@code https://example.com/authz/issuer1/oauth2/token} — resolves the issuer to {@code https://example.com/authz/issuer1}
  • 43 | *
  • {@code https://example.com/authz/issuer2/oauth2/token} — resolves the issuer to {@code https://example.com/authz/issuer2}
  • 44 | *
45 | * 46 | * @return {@link AuthorizationServerSettings#getIssuer()} if available, otherwise, resolves the issuer identifier from the "current" request 47 | */ 48 | String getIssuer(); 49 | 50 | /** 51 | * Returns the {@link AuthorizationServerSettings}. 52 | * 53 | * @return the {@link AuthorizationServerSettings} 54 | */ 55 | AuthorizationServerSettings getAuthorizationServerSettings(); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OAuth2EndpointUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.oidc.web.authentication; 17 | 18 | import java.util.Map; 19 | 20 | import jakarta.servlet.http.HttpServletRequest; 21 | 22 | import org.springframework.util.LinkedMultiValueMap; 23 | import org.springframework.util.MultiValueMap; 24 | import org.springframework.util.StringUtils; 25 | 26 | /** 27 | * Utility methods for the OAuth 2.0 Protocol Endpoints. 28 | * 29 | * @author Joe Grandja 30 | * @author Greg Li 31 | * @since 1.1.4 32 | */ 33 | final class OAuth2EndpointUtils { 34 | 35 | private OAuth2EndpointUtils() { 36 | } 37 | 38 | static MultiValueMap getFormParameters(HttpServletRequest request) { 39 | Map parameterMap = request.getParameterMap(); 40 | MultiValueMap parameters = new LinkedMultiValueMap<>(); 41 | parameterMap.forEach((key, values) -> { 42 | String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : ""; 43 | // If not query parameter then it's a form parameter 44 | if (!queryString.contains(key) && values.length > 0) { 45 | for (String value : values) { 46 | parameters.add(key, value); 47 | } 48 | } 49 | }); 50 | return parameters; 51 | } 52 | 53 | static MultiValueMap getQueryParameters(HttpServletRequest request) { 54 | Map parameterMap = request.getParameterMap(); 55 | MultiValueMap parameters = new LinkedMultiValueMap<>(); 56 | parameterMap.forEach((key, values) -> { 57 | String queryString = StringUtils.hasText(request.getQueryString()) ? request.getQueryString() : ""; 58 | if (queryString.contains(key) && values.length > 0) { 59 | for (String value : values) { 60 | parameters.add(key, value); 61 | } 62 | } 63 | }); 64 | return parameters; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /docs/src/main/java/sample/customclaims/authorities/CustomClaimsWithAuthoritiesConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package sample.customclaims.authorities; 17 | 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.security.core.authority.AuthorityUtils; 21 | import org.springframework.security.core.userdetails.User; 22 | import org.springframework.security.core.userdetails.UserDetails; 23 | import org.springframework.security.core.userdetails.UserDetailsService; 24 | import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; 25 | import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext; 26 | import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; 27 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 28 | 29 | import java.util.Collections; 30 | import java.util.Set; 31 | import java.util.stream.Collectors; 32 | 33 | @Configuration 34 | public class CustomClaimsWithAuthoritiesConfiguration { 35 | @Bean 36 | public UserDetailsService users() { 37 | UserDetails user = User.withDefaultPasswordEncoder() 38 | .username("user1") // <1> 39 | .password("password") 40 | .roles("user", "admin") // <2> 41 | .build(); 42 | return new InMemoryUserDetailsManager(user); 43 | } 44 | 45 | @Bean 46 | public OAuth2TokenCustomizer jwtTokenCustomizer() { // <3> 47 | return (context) -> { 48 | if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { // <4> 49 | context.getClaims().claims((claims) -> { // <5> 50 | Set roles = AuthorityUtils.authorityListToSet(context.getPrincipal().getAuthorities()) 51 | .stream() 52 | .map(c -> c.replaceFirst("^ROLE_", "")) 53 | .collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet)); // <6> 54 | claims.put("roles", roles); // <7> 55 | }); 56 | } 57 | }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimNames.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2023 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.springframework.security.oauth2.server.authorization.oidc; 17 | 18 | import org.springframework.security.oauth2.core.oidc.OidcIdToken; 19 | import org.springframework.security.oauth2.jose.jws.JwsAlgorithm; 20 | import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames; 21 | 22 | /** 23 | * The names of the "claims" defined by OpenID Connect Discovery 1.0 that can be returned 24 | * in the OpenID Provider Configuration Response. 25 | * 26 | * @author Daniel Garnier-Moiroux 27 | * @author Joe Grandja 28 | * @since 0.1.0 29 | * @see OAuth2AuthorizationServerMetadataClaimNames 30 | * @see 3. OpenID Provider Metadata 31 | */ 32 | public final class OidcProviderMetadataClaimNames extends OAuth2AuthorizationServerMetadataClaimNames { 33 | 34 | /** 35 | * {@code subject_types_supported} - the Subject Identifier types supported 36 | */ 37 | public static final String SUBJECT_TYPES_SUPPORTED = "subject_types_supported"; 38 | 39 | /** 40 | * {@code id_token_signing_alg_values_supported} - the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token} 41 | */ 42 | public static final String ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = "id_token_signing_alg_values_supported"; 43 | 44 | /** 45 | * {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint 46 | * @since 0.2.2 47 | */ 48 | public static final String USER_INFO_ENDPOINT = "userinfo_endpoint"; 49 | 50 | /** 51 | * {@code end_session_endpoint} - the {@code URL} of the OpenID Connect 1.0 End Session Endpoint 52 | * @since 1.1 53 | */ 54 | public static final String END_SESSION_ENDPOINT = "end_session_endpoint"; 55 | 56 | private OidcProviderMetadataClaimNames() { 57 | } 58 | 59 | } 60 | --------------------------------------------------------------------------------