Spring Authorization Server は、OAuth 2.1 と OpenID Connect 1.0 の認可サーバー(アクセストークンを発行するサーバー)を Spring で作るためのライブラリです。 Spring Boot 4 では、starter を入れて application.yml にクライアントを書くだけで、/oauth2/token などのエンドポイントが動きます。

spring:
  security:
    oauth2:
      authorizationserver:
        client:
          batch-client:
            registration:
              client-id: batch-client
              client-secret: "{noop}batch-secret"
              client-authentication-methods: [client_secret_basic]
              authorization-grant-types: [client_credentials]
              scopes: [orders.read]
curl -s -u batch-client:batch-secret \
  -d grant_type=client_credentials -d scope=orders.read \
  http://localhost:9000/oauth2/token

2025年11月の Spring Security 7.0 から、Spring Authorization Server は Spring Security 本体に統合されました。2026年10月時点の最新は Spring Security 7.1.1・Spring Boot 4.1.1 です。

版の組み合わせ 依存の書き方 備考
Spring Boot 4.x + Spring Security 7.x spring-boot-starter-security-oauth2-authorization-server 本記事の対象
同上(以前の starter 名) spring-boot-starter-oauth2-authorization-server 4.1.1 では非推奨と明記
Spring Boot 3.x + Spring Authorization Server 1.x spring-boot-starter-oauth2-authorization-server 1.5.8 が 1.x の最新

確認環境:Spring Security 7.1.1・Spring Boot 4.1.1 のソース(GitHub のタグ)とリファレンスで、設定項目・既定値・エンドポイントのパス・エラー文を確かめました。比較のため Spring Authorization Server 1.5.8 のソースも読んでいます。PKCE の値は OpenSSL 3.0.13 と Python 3.11 で計算し、RFC 7636 の付録 B の値と一致することを確かめました(確認日 2026年10月13日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Spring Authorization Server入門|最小構成とcurl例」

1. 依存関係

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.1.1</version>
  <relativePath/>
</parent>

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security-oauth2-authorization-server</artifactId>
  </dependency>
</dependencies>

Gradle の場合は implementation("org.springframework.boot:spring-boot-starter-security-oauth2-authorization-server") です。

この starter は、spring-boot-starter-security と spring-boot-starter-webmvc も一緒に入れます(4.1.1 の build.gradle)。Servlet(Spring MVC)のアプリでだけ自動設定が働き、WebFlux には対応していません(自動設定の条件が ConditionalOnWebApplication(type = SERVLET))。

2. 最小構成の application.yml(コード全文)

機械どうしの通信用(client_credentials)と、ログイン画面のある Web アプリ用(authorization_code+PKCE)の2つのクライアントを登録します。

server:
  port: 9000

spring:
  security:
    user:                       # ログイン画面で使うユーザー(開発用)
      name: user
      password: password
    oauth2:
      authorizationserver:
        # issuer: http://localhost:9000   # 書かないと、リクエストのURLから決まる
        client:
          batch-client:                     # ① サーバー間通信
            registration:
              client-id: batch-client
              client-secret: "{noop}batch-secret"
              client-authentication-methods:
                - client_secret_basic
              authorization-grant-types:
                - client_credentials
              scopes:
                - orders.read
                - orders.write
            token:
              access-token-time-to-live: 10m
          web-client:                       # ② ユーザーがログインする Web アプリ
            registration:
              client-id: web-client
              client-secret: "{noop}web-secret"
              client-authentication-methods:
                - client_secret_basic
              authorization-grant-types:
                - authorization_code
                - refresh_token
              redirect-uris:
                - http://127.0.0.1:8080/login/oauth2/code/web-client
              post-logout-redirect-uris:
                - http://127.0.0.1:8080/
              scopes:
                - openid
                - profile
                - orders.read
            require-authorization-consent: true   # 同意画面を出す

logging:
  level:
    org.springframework.security: trace   # 失敗の理由がログに出る(開発時)
package com.example.authserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AuthServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AuthServerApplication.class, args);
    }
}

Java のコードはこれだけです。 Spring Boot 4.1.1 の自動設定が、次の Bean を用意します。

Bean 自動設定の中身(4.1.1 のソース)
RegisteredClientRepository YAML のクライアントを入れた InMemoryRegisteredClientRepository
SecurityFilterChain(認可サーバー用) 認可サーバーのエンドポイント+ OIDC を有効化。HTML のリクエストは /login へ転送
SecurityFilterChain(その他) すべて認証必須+フォームログイン
JWKSource 起動のたびに作る 2048 ビットの RSA 鍵(kid は UUID)
JwtDecoder 上の鍵で JWT を検証
AuthorizationServerSettings エンドポイントのパス(下の一覧)

鍵が起動のたびに作り直されるので、再起動すると発行済みのトークンは検証できなくなります。 本番では JWKSource を自分で Bean にして、鍵を保管場所から読みます(6章)。

クライアント設定の既定値

spring.security.oauth2.authorizationserver.client.<名前>. の下に書く項目です(4.1.1 の OAuth2AuthorizationServerProperties)。

プロパティ 既定値 内容
require-proof-key true PKCE を必須にする
require-authorization-consent false 同意画面を出す
token.access-token-time-to-live 5m アクセストークンの有効期間
token.access-token-format self-contained JWT。reference で不透明な文字列
token.authorization-code-time-to-live 5m 認可コードの有効期間
token.refresh-token-time-to-live 60m リフレッシュトークンの有効期間
token.reuse-refresh-tokens true 更新時に同じリフレッシュトークンを使い続ける
token.id-token-signature-algorithm RS256 ID トークンの署名方式

client-secret は、PasswordEncoder で照合できる形で書きます。 {noop} は平文(開発用)です。本番では {bcrypt}$2a$10$... のように符号化した値を書きます。

3. エンドポイント一覧

4.1.1 の既定のパスと、最初から有効かどうかです。 パスは spring.security.oauth2.authorizationserver.endpoint.* で変えられます。

エンドポイント 既定のパス 既定で有効 用途
認可 /oauth2/authorize ○ ブラウザでログイン・同意して認可コードを得る
トークン /oauth2/token ○ コードやクライアント資格情報をトークンに交換
JWK Set /oauth2/jwks ○(JWKSource があるとき) 署名の公開鍵
トークン検査(Introspection) /oauth2/introspect ○ トークンが有効か問い合わせる
トークン失効(Revocation) /oauth2/revoke ○ トークンを無効にする
認可サーバーのメタデータ /.well-known/oauth-authorization-server ○ エンドポイントの一覧(RFC 8414)
OIDC の設定情報 /.well-known/openid-configuration ○(OIDC 有効時) OpenID Connect の Discovery
UserInfo /userinfo ○(OIDC 有効時) ログインしたユーザーの情報
OIDC ログアウト /connect/logout ○(OIDC 有効時) RP-Initiated Logout
PAR /oauth2/par 設定が要る 認可リクエストを先に POST で登録
デバイス認可 /oauth2/device_authorization 設定が要る テレビなど入力しにくい機器向け
デバイス確認 /oauth2/device_verification 設定が要る 上の確認画面
クライアント登録 /oauth2/register 設定が要る 動的クライアント登録(RFC 7591)
OIDC クライアント登録 /connect/register 設定が要る OIDC の動的登録

「既定で有効」は、Spring Security 7.1.1 の OAuth2AuthorizationServerConfigurer が最初から組み込む部品と、oidc() で加わる部品から判断しました。Spring Boot の自動設定は oidc() を有効にしています。

まずメタデータを取ると、どのエンドポイントがどこにあるか分かります。

curl -s http://localhost:9000/.well-known/oauth-authorization-server

4. client_credentials(サーバー間通信)

ユーザーが関わらない、バッチや別サービスからの呼び出しに使う方式です。 クライアント ID と秘密情報を Basic 認証で送ります。

curl -s -u batch-client:batch-secret \
  -d grant_type=client_credentials \
  -d scope="orders.read orders.write" \
  http://localhost:9000/oauth2/token

送られる HTTP は次のとおりです。Authorization の値は batch-client:batch-secret を Base64 にしたものです。

POST /oauth2/token HTTP/1.1
Host: localhost:9000
Authorization: Basic YmF0Y2gtY2xpZW50OmJhdGNoLXNlY3JldA==
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=orders.read%20orders.write

応答は RFC 6749 の形です(値は例。access_token は JWT)。

{
  "access_token": "eyJraWQiOiI...",
  "scope": "orders.read orders.write",
  "token_type": "Bearer",
  "expires_in": 599
}

expires_in は、上の YAML の access-token-time-to-live: 10m(600秒)から、発行からの経過秒を引いた値になります。client_credentials では、リフレッシュトークンは発行されません。

アクセストークン(JWT)の中身は、7.1.1 の JwtGenerator のソースでは次の項目です。

クレーム 中身
iss 発行者(issuer)
sub client_credentials ではクライアント ID、authorization_code ではユーザー名
aud クライアント ID(配列)
nbf・iat 発行時刻
exp 期限
jti トークンごとの UUID
scope 許可されたスコープ(配列)

JWT の仕組みは、関連記事「JWTとは|仕組みと、安全に使うための注意点」で扱っています。

よくある失敗

状況 応答
秘密情報が違う・クライアントが無い 401 と {"error":"invalid_client"}
登録していない grant_type を使った 400 と {"error":"unauthorized_client"}
登録していないスコープを求めた 400 と {"error":"invalid_scope"}

logging.level.org.springframework.security: trace にしておくと、拒否の理由がログに出ます。

5. authorization_code + PKCE(ユーザーのログイン)

ユーザーがブラウザでログインし、アプリがその人の代わりに API を呼ぶ方式です。 Spring Security 7 では、PKCE(認可コードの横取りを防ぐ仕組み)が既定で必須です。

Spring Security 7 で変わった既定値

項目 Spring Authorization Server 1.5.8 Spring Security 7.1.1
ClientSettings の requireProofKey の既定 false true
秘密情報を持つクライアントの PKCE 任意 必須(code_challenge が無いとエラー)

ソースの ClientSettings.builder() が、1.5.8 では requireProofKey(false)、7.1.1 では requireProofKey(true) です。1.x から上げると、PKCE を送っていない Web アプリのログインが invalid_request で止まります。同じ 7.1.1 の OAuth2 Client(oauth2Login)は、クライアント側の ClientSettings も既定が requireProofKey=true で、PKCE を付けて送ります。Spring Security 6.x 以前のクライアントや、自作のクライアントからつなぐときは、PKCE を付けるか、サーバー側でそのクライアントだけ require-proof-key: false にします。

手順1:code_verifier と code_challenge を作る

code_verifier はランダムな文字列、code_challenge はその SHA-256 を Base64URL(= なし)にしたものです。

CODE_VERIFIER=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
CODE_CHALLENGE=$(printf '%s' "$CODE_VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')
echo "$CODE_VERIFIER" "$CODE_CHALLENGE"

RFC 7636 の付録 B の値で計算すると、仕様どおりの結果になりました。

code_verifier : dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
code_challenge: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM

code_challenge_method は S256 だけが受け付けられます。 7.1.1 のソースでは、plain や指定なしは invalid_request です。

手順2:ブラウザで認可エンドポイントを開く

http://localhost:9000/oauth2/authorize?response_type=code
  &client_id=web-client
  &scope=openid%20profile%20orders.read
  &redirect_uri=http://127.0.0.1:8080/login/oauth2/code/web-client
  &state=xyz123
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256

(実際は1行で書きます。)ログイン画面(/login)で user / password を入れ、同意画面でスコープを選ぶと、次の URL に戻されます。

http://127.0.0.1:8080/login/oauth2/code/web-client?code=XXXXXXXX&state=xyz123

8080 番で何も動いていなければブラウザはエラーを出しますが、アドレスバーの code= の値をそのまま使えます。

手順3:コードをトークンに交換する

curl -s -u web-client:web-secret \
  -d grant_type=authorization_code \
  -d code=XXXXXXXX \
  -d redirect_uri=http://127.0.0.1:8080/login/oauth2/code/web-client \
  -d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk \
  http://localhost:9000/oauth2/token

openid スコープを含めたので、応答に ID トークンが付きます(値は例)。

{
  "access_token": "eyJraWQiOi...",
  "refresh_token": "Xq3...",
  "scope": "openid profile orders.read",
  "id_token": "eyJraWQiOi...",
  "token_type": "Bearer",
  "expires_in": 299
}

PKCE とリダイレクトのエラー

状況 エラー(7.1.1 のソース)
認可リクエストに code_challenge が無い invalid_request、説明 OAuth 2.0 Parameter: code_challenge
code_challenge_method が S256 以外 invalid_request、説明 OAuth 2.0 Parameter: code_challenge_method
code_verifier が合わない invalid_grant、説明 Client authentication failed: code_verifier
redirect_uri が登録と完全一致しない invalid_request(redirect_uri)

redirect_uri は完全一致で比べます。 ただし 127.0.0.1 などのループバックの IP アドレスだけは、ポート番号が違っても通します(OAuth 2.1 の草案に沿った処理とソースにコメントがあります)。localhost はループバックの IP として扱われないので、完全一致が必要です。リファレンスの例が 127.0.0.1 を使っているのはこのためです。

リフレッシュトークンで更新する

curl -s -u web-client:web-secret \
  -d grant_type=refresh_token -d refresh_token=Xq3... \
  http://localhost:9000/oauth2/token

UserInfo・Introspection・Revocation

# ユーザー情報(アクセストークンに openid スコープが必要)
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:9000/userinfo

# トークンが有効か(クライアント認証が必要)
curl -s -u batch-client:batch-secret -d token=$ACCESS_TOKEN http://localhost:9000/oauth2/introspect

# 失効させる
curl -s -u batch-client:batch-secret -d token=$ACCESS_TOKEN http://localhost:9000/oauth2/revoke

Introspection の応答は RFC 7662 の形で、有効なら "active": true とクレームが、無効なら {"active":false} が返ります。

6. YAML ではなく Java で書く(本番に近い形)

クライアントを DB に置く、鍵を固定する、などは Bean を自分で定義します。 同じ型の Bean があると、Spring Boot の自動設定は引っ込みます。

package com.example.authserver;

import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;

@Configuration(proxyBeanMethods = false)
public class AuthServerConfig {

    @Bean
    RegisteredClientRepository registeredClientRepository() {
        RegisteredClient batch = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("batch-client")
                .clientSecret("{noop}batch-secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .scope("orders.read")
                .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofMinutes(10)).build())
                .build();
        // 本番は JdbcRegisteredClientRepository などに替える
        return new InMemoryRegisteredClientRepository(batch);
    }

    @Bean
    JWKSource<SecurityContext> jwkSource() throws Exception {
        // 例として起動時に生成。本番は KMS や鍵ファイルから読み、kid を固定する
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(2048);
        KeyPair keyPair = generator.generateKeyPair();
        RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) keyPair.getPublic())
                .privateKey((RSAPrivateKey) keyPair.getPrivate())
                .keyID("auth-key-2026-10")
                .build();
        return new ImmutableJWKSet<>(new JWKSet(rsaKey));
    }

    @Bean
    AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder()
                .issuer("https://auth.example.com")   // トークンの iss と Discovery に使われる
                .build();
    }
}

issuer を決めておかないと、アクセスされた URL から issuer が決まります。ロードバランサーの内側と外側で URL が違うと、リソースサーバーの検証で iss が合わなくなるので、本番では固定します。

7. リソースサーバー(API 側)でトークンを検証する

発行したトークンを受け取る API 側は、spring-boot-starter-security-oauth2-resource-server を入れて、issuer を書きます。

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:9000

起動時に issuer-uri の Discovery から /oauth2/jwks を見つけ、署名を検証します。スコープは SCOPE_orders.read という権限になります。API 側の書き方は、関連記事「Spring Security JWT認証の実装|Boot 4対応の設定コード全文」で扱っています。

よくある質問

Q. Keycloak との違いは? A. Keycloak は完成した製品で、管理画面・ユーザー管理・外部 IdP 連携が最初からあります。Spring Authorization Server はライブラリで、ユーザーの保存先や画面、トークンの中身を Java で決めます。Keycloak は関連記事「Keycloak入門|OIDCログインとJWT検証を最短で構築」で扱っています。

Q. OAuth 2.0 と OpenID Connect の違いは? A. OAuth 2.0 は API を呼ぶ許可(アクセストークン)、OpenID Connect はその上に「誰がログインしたか」(ID トークン・UserInfo)を足した仕様です。関連記事「OAuth 2.0とOpenID Connectの違いをわかりやすく解説」で扱っています。

Q. password グラント(ユーザー名とパスワードを直接送る方式)は使えますか。 A. リファレンスのトークンエンドポイントの説明で対応するのは authorization_code・refresh_token・client_credentials・デバイスコード・トークン交換で、password グラントはありません。OAuth 2.1 でも外されています。

Q. ログイン画面を自作したい。 A. 2つ目の SecurityFilterChain で formLogin(form -> form.loginPage("/login")) を指定し、自分の画面を出します。Spring Security の基本は、関連記事「Spring Security入門|SecurityFilterChainとDB認証」で扱っています。

まとめ

  • Spring Boot 4 では spring-boot-starter-security-oauth2-authorization-server。Spring Security 7 に統合された
  • YAML にクライアントを書くだけで、/oauth2/token などが動く。鍵は起動のたびに作られる
  • client_credentials は -u ID:秘密 で /oauth2/token に POST。リフレッシュトークンは出ない
  • Spring Security 7 では PKCE が既定で必須(1.x は任意)。S256 だけ
  • redirect_uri は完全一致。ループバック IP だけポート違いを許す。本番は issuer と鍵を固定する

参考・出典

確認日はいずれも 2026年10月13日です。

  • Spring Security Reference「Authorization Server: Getting Started」:https://docs.spring.io/spring-security/reference/servlet/oauth2/authorization-server/getting-started.html
  • Spring Security Reference「Protocol Endpoints」:https://docs.spring.io/spring-security/reference/servlet/oauth2/authorization-server/protocol-endpoints.html
  • Spring Security Reference「Core Model / Components」:https://docs.spring.io/spring-security/reference/servlet/oauth2/authorization-server/core-model-components.html
  • Spring Boot Reference「OAuth2」(Authorization Server):https://docs.spring.io/spring-boot/reference/security/oauth2.html
  • Spring Security ソース(タグ 7.1.1):https://github.com/spring-projects/spring-security/tree/7.1.1
  • RFC 6749「The OAuth 2.0 Authorization Framework」:https://datatracker.ietf.org/doc/html/rfc6749
  • RFC 7636「Proof Key for Code Exchange」:https://datatracker.ietf.org/doc/html/rfc7636
  • RFC 8414「OAuth 2.0 Authorization Server Metadata」:https://datatracker.ietf.org/doc/html/rfc8414

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Spring Authorization Server入門|最小構成とcurl例」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?