- Spring
Spring Authorization Server入門|最小構成とcurl例
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期連続の黒字決算。