Line data Source code
1 : // Package controller はエンドポイント処理を担当します。
2 : package controller
3 :
4 : import (
5 : "net/http"
6 :
7 : "github.com/gin-gonic/gin"
8 :
9 : fba "resume/internal/adapter/gateway/firebase"
10 : "resume/internal/adapter/http/handlerutil"
11 : "resume/internal/adapter/http/middleware"
12 : "resume/internal/shared/apperr"
13 : "resume/internal/shared/util"
14 : ucauth "resume/internal/usecase/auth"
15 : )
16 :
17 : // AuthHandler は認証用のハンドラです。
18 : type AuthHandler struct {
19 : fb fba.Auth
20 : uc ucauth.Usecase
21 : }
22 :
23 : // NewAuthHandler は AuthHandler の新しいインスタンスを生成して返します。
24 : // 認証関連の HTTP リクエストを処理するためのハンドラを初期化します。
25 0 : func NewAuthHandler(fb fba.Auth, uc ucauth.Usecase) *AuthHandler {
26 0 : return &AuthHandler{fb: fb, uc: uc}
27 0 : }
28 :
29 : // UserLogin はユーザーのログイン情報を返します
30 : // コンテキストから、idTokenを取得しユーザー情報の
31 0 : func (h *AuthHandler) UserLogin(c *gin.Context) {
32 0 : idToken, ok := middleware.Bearer(c.GetHeader("Authorization"))
33 0 : if !ok || idToken == "" {
34 0 : handlerutil.WriteError(c,
35 0 : apperr.New(apperr.CodeBadRequest, "missing or invalid Authorization header", nil))
36 0 : return
37 0 : }
38 :
39 : // Verify token
40 0 : tok, err := h.fb.VerifyIDToken(c.Request.Context(), idToken)
41 0 : if err != nil {
42 0 : handlerutil.WriteError(c,
43 0 : apperr.Wrap(apperr.CodeUnauthorized, "invalid token", err, nil))
44 0 : return
45 0 : }
46 :
47 : // Fetch Firebase User
48 0 : ur, err := h.fb.GetUser(c.Request.Context(), tok.UID)
49 0 : if err != nil {
50 0 : handlerutil.WriteError(c,
51 0 : apperr.Wrap(apperr.CodeUnauthorized, "failed to fetch firebase user", err, nil))
52 0 : return
53 0 : }
54 :
55 : // Build Providers From UserRecord
56 0 : providers := make([]ucauth.ProviderInfo, 0, len(ur.ProviderUserInfo))
57 0 : for _, p := range ur.ProviderUserInfo {
58 0 : // p.ProviderID: "google.com" | "github.com" | "password" | ...
59 0 : providers = append(providers, ucauth.ProviderInfo{
60 0 : Provider: util.SafeStr(p.ProviderID),
61 0 : ProviderUserID: util.SafeStr(p.UID),
62 0 : ProviderDisplayName: util.FallbackStr(p.DisplayName, ur.DisplayName, "unknown"),
63 0 : EmailAtSignup: util.OptPtr(p.Email),
64 0 : })
65 0 : }
66 :
67 : // Build input DTO (users/auth_identitiesに寄せた最小構成)
68 0 : in := ucauth.UpsertUserFromFirebaseInput{
69 0 : IDToken: idToken,
70 0 : UID: ur.UID,
71 0 : Email: util.OptPtr(ur.Email),
72 0 : EmailVerified: ur.EmailVerified,
73 0 : DisplayName: util.OptPtr(ur.DisplayName),
74 0 : PhotoURL: util.OptPtr(ur.PhotoURL),
75 0 : Providers: providers,
76 0 : }
77 0 :
78 0 : // Call UseCase (内部でlast_login_at を now に更新 & Upsert)
79 0 : out, err := h.uc.UpsertUserFromFirebase(c.Request.Context(), in)
80 0 : if err != nil {
81 0 : handlerutil.WriteError(c, err) // usecase 側が apperr.Error を返すのでそのまま渡す
82 0 : return
83 0 : }
84 :
85 0 : c.JSON(http.StatusOK, out)
86 : }
|