hydraのjwt検証をしようとしてハマった箇所
更新: 2026年1月21日
hydraのjwt検証をdev環境で行ったところ、issuerでミス
状況
- ゲームサーバーとAPIサーバーがそれぞれのアクセストークンを持っている
- 今まではAPIサーバー側でマッパーを所持していてAPIサーバーアクセス時にゲームサーバーにアクセスするのに必要なトークンを渡していた
- 2つのトークンを保持し続けるのは片方の有効期限が切れたとき(特にゲームサーバー側)で問題があるため対応を行う
方針
- ゲームサーバー側でもHydraによるアクセストークンを受け付ける
- その際、APIサーバーからの通信のときのみ許可とする
- jwks検証を行う
問題
内部通信と外部URLの不一致: ECS内部でhttp://hogehoge.localhost:4444 として通信していても、トークン内の iss は https://<公開時のURL (外部公開URL)になっている必要があった。(これはdev環境でAWS上のECSからアクセスしようとした際にパブリックネットワークを経由するのが嫌だったため) AIにコード記述を任せていたら、ここの環境変数分離は行っていなかったので、issが合わずエラーとなっていた)
対策
hydra:adminに対して、hydraAdminURL + “/oauth2/introspect” にtokenをわたすことで検証がおこなえるので一旦こちらで対応することに。
// callIntrospection calls Hydra's introspection endpoint
func (c *IntrospectionCache) callIntrospection(token string) (string, int64, error) {
// Prepare request
data := url.Values{}
data.Set("token", token)
req, err := http.NewRequest("POST", hydraIntrospectURL, strings.NewReader(data.Encode()))
if err != nil {
return "", 0, fmt.Errorf("failed to create introspection request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
// Execute request
resp, err := c.client.Do(req)
if err != nil {
return "", 0, fmt.Errorf("failed to call introspection endpoint: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := ioutil.ReadAll(resp.Body)
return "", 0, fmt.Errorf("introspection endpoint returned status %d: %s", resp.StatusCode, string(body))
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", 0, fmt.Errorf("failed to read introspection response: %w", err)
}
var introspection IntrospectionResponse
if err := json.Unmarshal(body, &introspection); err != nil {
return "", 0, fmt.Errorf("failed to parse introspection response: %w", err)
}
// Check if token is active
if !introspection.Active {
return "", 0, fmt.Errorf("token is not active or has been revoked")
}
// Validate expiration
if introspection.Exp <= time.Now().Unix() {
return "", 0, fmt.Errorf("token has expired")
}
// Validate wallet address format (Ethereum address)
if !ethAddressPattern.MatchString(introspection.Sub) {
return "", 0, fmt.Errorf("invalid wallet address format in token: %s", introspection.Sub)
}
return introspection.Sub, introspection.Exp, nil
}
というわけで、AIに任せてて大分いい感じかつ、高速に仕事は進むわけだけど、前提知識持ってないと詰まるなぁといった感じでした(3時間スタック)