メインコンテンツまでスキップ

クライアントクレデンシャルフローによるトークンの取得

このページでは、デジタル認証アプリサービス API を使用するための最初のステップとして、アクセストークンを取得する方法を説明します。

トークンの取得には、OpenID Connect のクライアントクレデンシャルフロー(クライアントアプリケーションが自身の資格情報を使用してアクセストークンを取得する認証方式)を使用します。

シーケンス

実装例

package main

import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"time"

"github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jwt"
)

// クライアント情報
const (
// デジタル庁から発行されたクライアント ID です。ご自身のクライアント ID で置き換えてください。
clientID = "<YOUR_CLIENT_ID>"
// デジタル認証アプリサービスのトークンエンドポイント URL
tokenURL = "https://sb-auth-and-sign.go.jp/api/realms/main/protocol/openid-connect/token"
// クライアント認証用の秘密鍵(JWK 形式)。ご自身の秘密鍵で置き換えてください。
jwkJSON = `<YOUR_SECRET_JWK>`
)

// client_assertion JWT を生成する関数
// クライアント認証に使用する JWT トークンを作成します
func generateJWT(clientID, tokenURL string, key jwk.Key) (string, error) {
tok, err := jwt.NewBuilder().
// iss: JWT の発行者(クライアント ID)
Issuer(clientID).
// sub: JWT の主体(クライアント ID)
Subject(clientID).
// aud: JWT の対象者(トークンエンドポイント URL)
Audience([]string{tokenURL}).
// jti: JWT の一意識別子(リプレイアタック対策)
JwtID(fmt.Sprintf("%s_%d", clientID, time.Now().UnixNano())).
IssuedAt(time.Now()).
Expiration(time.Now().Add(5 * time.Minute)).
Build()
if err != nil {
return "", fmt.Errorf("failed to build token: %w", err)
}

// JWT を ES256 アルゴリズムで署名
signed, err := jwt.Sign(tok, jwt.WithKey(jwa.ES256, key))
if err != nil {
return "", fmt.Errorf("failed to sign token: %w", err)
}

return string(signed), nil
}

func run() error {
key, err := jwk.ParseKey([]byte(jwkJSON))
if err != nil {
return fmt.Errorf("failed to parse JWK: %w", err)
}

// クライアント認証用の JWT (client_assertion) を生成します。
clientAssertion, err := generateJWT(clientID, tokenURL, key)
if err != nil {
return fmt.Errorf("failed to generate JWT: %w", err)
}

// OAuth 2.0 トークンリクエストのパラメータを設定
reqBody := url.Values{}
// クライアントクレデンシャルフローでは grant_type に "client_credentials" を指定
reqBody.Set("grant_type", "client_credentials")
reqBody.Set("client_id", clientID)
reqBody.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer")
reqBody.Set("client_assertion", clientAssertion)
// 署名 API を使用するためのスコープを指定
reqBody.Set("scope", "sign")

// トークンエンドポイントにリクエストを送信します。
resp, err := http.Post(tokenURL, "application/x-www-form-urlencoded", bytes.NewBufferString(reqBody.Encode()))
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()

// レスポンスを読み込みます。
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
}

fmt.Println(string(respBody))
return nil
}

func main() {
if err := run(); err != nil {
panic(err)
}
}

実行結果

アクセストークンの取得に成功すると、以下のようなレスポンスが返されます:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhVFN4RHpxM1B6c2dhak0wN3YzWmxGSm8tajBWUGdaVWQ1d0NPOUJydkx3In0...",
"expires_in": 900,
"refresh_expires_in": 0,
"token_type": "Bearer",
"not_before_policy": 0,
"scope": "openid offline_access sign"
}

次のステップ

アクセストークンを取得できたら、署名トランザクションの開始を行いましょう。

その他、デジタル認証アプリサービス API の使い方やエラーの詳細等については、デジタル認証アプリのマニュアルをご覧ください。