package auth import ( "context" "strings" errs "gateway/internal/library/errors" "gateway/internal/library/errors/code" "gateway/internal/library/zitadel" domauth "gateway/internal/model/auth/domain/usecase" memberdom "gateway/internal/model/member/domain" memberenum "gateway/internal/model/member/domain/enum" dommember "gateway/internal/model/member/domain/usecase" "gateway/internal/svc" "gateway/internal/types" ) func issueAuthToken(ctx context.Context, sc *svc.ServiceContext, tenantID, uid string) (*types.AuthTokenData, error) { if sc.AuthToken == nil { return nil, errb.SysNotImplemented("auth token not configured") } pair, err := sc.AuthToken.IssuePair(ctx, &domauth.IssuePairRequest{ TenantID: tenantID, UID: uid, }) if err != nil { return nil, err } return &types.AuthTokenData{ AccessToken: pair.AccessToken, RefreshToken: pair.RefreshToken, ExpiresIn: pair.ExpiresIn, UID: uid, TokenType: pair.TokenType, }, nil } func tokenDataFromRefresh(ctx context.Context, sc *svc.ServiceContext, refreshToken string) (*types.AuthTokenData, error) { if sc.AuthToken == nil { return nil, errb.SysNotImplemented("auth token not configured") } pair, err := sc.AuthToken.Refresh(ctx, refreshToken) if err != nil { return nil, err } claims, err := sc.AuthToken.ParseAccessToken(ctx, pair.AccessToken) if err != nil { return nil, err } return &types.AuthTokenData{ AccessToken: pair.AccessToken, RefreshToken: pair.RefreshToken, ExpiresIn: pair.ExpiresIn, UID: claims.UID, TokenType: pair.TokenType, }, nil } func zitadelIdentityFromToken(ctx context.Context, client *zitadel.Client, tok *zitadel.TokenResult) (*zitadel.IDTokenClaims, error) { if tok == nil { return nil, errb.SvcThirdParty("empty token result") } if tok.Subject != "" { return &zitadel.IDTokenClaims{ Sub: tok.Subject, Email: tok.Email, }, nil } if tok.IDToken != "" { claims, err := zitadel.ParseIDTokenClaims(tok.IDToken) if err != nil { return nil, errb.SvcThirdParty("parse id_token failed").WithCause(err) } return claims, nil } claims, err := client.FetchUserInfo(ctx, tok.AccessToken) if err != nil { return nil, wrapZitadelErr(err) } return claims, nil } func memberForLogin(ctx context.Context, sc *svc.ServiceContext, tenantID, zitadelSub string) (*dommember.MemberDTO, error) { if sc.MemberProfile == nil { return nil, errb.SysNotImplemented("member profile not configured") } dto, err := sc.MemberProfile.GetByZitadelUserID(ctx, tenantID, zitadelSub) if err != nil { if e := errs.FromError(err); e != nil && e.Category() == code.ResNotFound { return nil, errb.AuthUnauthorized("invalid credentials").WithCause(memberdom.ErrNotFound) } return nil, err } if err := ensureLoginEligible(dto.Status); err != nil { return nil, err } return dto, nil } func ensureLoginEligible(status memberenum.MemberStatus) error { switch status { case memberenum.MemberStatusActive: return nil case memberenum.MemberStatusUnverified: return errb.AuthForbidden("account is not verified") case memberenum.MemberStatusSuspended: return errb.AuthForbidden("account is suspended") case memberenum.MemberStatusDeleted: return errb.AuthUnauthorized("invalid credentials") default: return errb.AuthForbidden("account is not allowed to login") } } func normalizeLoginEmail(email string) string { return strings.TrimSpace(strings.ToLower(email)) } func requireLoginDeps(sc *svc.ServiceContext) error { if sc.Zitadel == nil { return errb.SysNotImplemented("zitadel not configured") } if sc.MemberProfile == nil { return errb.SysNotImplemented("member profile not configured") } return nil } func isMemberNotFound(err error) bool { e := errs.FromError(err) return e != nil && e.Category() == code.ResNotFound }