Line data Source code
1 : package middleware
2 :
3 : import (
4 : "net/http"
5 : "strings"
6 :
7 : "github.com/gin-gonic/gin"
8 :
9 : "resume/internal/adapter/gateway/firebase"
10 : "resume/internal/adapter/http/handlerutil"
11 : "resume/internal/shared/apperr"
12 : "resume/internal/shared/ctx/auth"
13 : )
14 :
15 : // Bearer extracts the token from an Authorization header in "Bearer <token>" format.
16 : // It returns the token and true if successful, otherwise an empty string and false.
17 3 : func Bearer(h string) (string, bool) {
18 6 : if h == "" {
19 3 : return "", false
20 3 : }
21 2 : parts := strings.SplitN(h, " ", 2)
22 2 : if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
23 0 : return "", false
24 0 : }
25 2 : return parts[1], true
26 : }
27 :
28 : // InjectAuth は Authorization ヘッダーの Bearer トークンを検証し、
29 : // 検証に成功した場合は認証クレーム(UID と email)をリクエストコンテキストへ注入します。
30 2 : func InjectAuth(fb firebase.Auth) gin.HandlerFunc {
31 4 : return func(c *gin.Context) {
32 2 : tok, ok := Bearer(c.GetHeader("Authorization"))
33 3 : if !ok {
34 1 : c.Next()
35 1 : return
36 1 : }
37 1 : t, err := fb.VerifyIDToken(c.Request.Context(), tok)
38 1 : if err != nil {
39 0 : // 無効なら素通り or 401 を返すかは方針次第。ここでは素通りに。
40 0 : c.Next()
41 0 : return
42 0 : }
43 1 : email := ""
44 2 : if v, ok := t.Claims["email"].(string); ok {
45 1 : email = v
46 1 : }
47 1 : ctx := auth.With(c.Request.Context(), auth.Claims{
48 1 : UID: t.UID, Email: email,
49 1 : })
50 1 : c.Request = c.Request.WithContext(ctx)
51 1 : c.Next()
52 : }
53 : }
54 :
55 : // RequireAuth は Bearer トークンの存在と有効性を強制し、
56 : // 失敗した場合は 401 を返して処理を中断します。成功時は認証クレームを注入します。
57 3 : func RequireAuth(fb firebase.Auth) gin.HandlerFunc {
58 5 : return func(c *gin.Context) {
59 2 : // ★ プリフライトは通す
60 2 : if c.Request.Method == http.MethodOptions {
61 0 : c.Next()
62 0 : return
63 0 : }
64 :
65 2 : tok, ok := Bearer(c.GetHeader("Authorization"))
66 4 : if !ok {
67 2 : handlerutil.WriteError(c, apperr.New(apperr.CodeUnauthorized, "unauthorized", nil))
68 2 : return
69 2 : }
70 :
71 1 : t, err := fb.VerifyIDToken(c.Request.Context(), tok)
72 1 : if err != nil {
73 0 : handlerutil.WriteError(
74 0 : c,
75 0 : apperr.Wrap(apperr.CodeUnauthorized, "invalid token", err, nil),
76 0 : )
77 0 : return
78 0 : }
79 :
80 1 : email := ""
81 2 : if v, ok := t.Claims["email"].(string); ok {
82 1 : email = v
83 1 : }
84 1 : ctx := auth.With(c.Request.Context(), auth.Claims{UID: t.UID, Email: email})
85 1 : c.Request = c.Request.WithContext(ctx)
86 1 : c.Next()
87 : }
88 : }
|