1
0
forked from baron/baron-sso

audit 로그 개선. kratos 코드발급 링크로 전송까지 진행 완료 #104

This commit is contained in:
Lectom C Han
2026-01-29 01:20:19 +09:00
parent ff17259117
commit b88de7ec91
46 changed files with 2843 additions and 585 deletions

View File

@@ -145,6 +145,101 @@ func (d *DescopeProvider) SignIn(loginID, password string) (*domain.AuthInfo, er
return res, nil
}
// UserExists는 loginID(이메일/전화번호) 기준으로 사용자가 있는지 확인합니다.
func (d *DescopeProvider) UserExists(loginID string) (bool, error) {
if d.Client == nil {
return false, fmt.Errorf("descope provider: client is nil")
}
ctx := context.Background()
if strings.Contains(loginID, "@") {
user, err := d.Client.Management.User().Load(ctx, loginID)
if err != nil {
if isDescopeNotFound(err) {
return false, nil
}
return false, err
}
return user != nil, nil
}
phone := normalizePhone(loginID)
searchOptions := &descope.UserSearchOptions{
Phones: []string{phone},
Limit: 1,
}
users, _, err := d.Client.Management.User().SearchAll(ctx, searchOptions)
if err != nil {
return false, err
}
return len(users) > 0, nil
}
// IssueSession은 비밀번호 없이 로그인 세션을 발급합니다.
func (d *DescopeProvider) IssueSession(loginID string) (*domain.AuthInfo, error) {
if d.Client == nil {
return nil, fmt.Errorf("descope provider: client is nil")
}
ctx := context.Background()
targetLoginID, err := d.resolveLoginID(loginID)
if err != nil {
return nil, err
}
embeddedToken, err := d.Client.Management.User().GenerateEmbeddedLink(ctx, targetLoginID, nil, 0)
if err != nil {
return nil, fmt.Errorf("descope provider: generate embedded link failed: %w", err)
}
authInfo, err := d.Client.Auth.MagicLink().Verify(ctx, embeddedToken, nil)
if err != nil {
return nil, fmt.Errorf("descope provider: magic link verify failed: %w", err)
}
res := &domain.AuthInfo{
SessionToken: &domain.Token{
JWT: authInfo.SessionToken.JWT,
Expiration: time.Unix(authInfo.SessionToken.Expiration, 0),
},
Subject: authInfo.User.UserID,
}
if authInfo.RefreshToken != nil {
res.RefreshToken = &domain.Token{
JWT: authInfo.RefreshToken.JWT,
Expiration: time.Unix(authInfo.RefreshToken.Expiration, 0),
}
}
return res, nil
}
func (d *DescopeProvider) InitiateLinkLogin(loginID, returnTo string) (*domain.LinkLoginInit, error) {
return nil, domain.ErrNotSupported
}
func (d *DescopeProvider) VerifyLoginCode(loginID, flowID, code string) (*domain.AuthInfo, error) {
return nil, domain.ErrNotSupported
}
// GetPasswordPolicy는 Descope 비밀번호 정책을 반환합니다.
func (d *DescopeProvider) GetPasswordPolicy() (*domain.PasswordPolicy, error) {
if d.Client == nil {
return nil, fmt.Errorf("descope provider: client is nil")
}
policy, err := d.Client.Auth.Password().GetPasswordPolicy(context.Background())
if err != nil {
return nil, err
}
return &domain.PasswordPolicy{
MinLength: int(policy.MinLength),
Lowercase: policy.Lowercase,
Uppercase: policy.Uppercase,
Number: policy.Number,
NonAlphanumeric: policy.NonAlphanumeric,
MinCharacterTypes: 0,
}, nil
}
func (d *DescopeProvider) InitiatePasswordReset(loginID, redirectUrl string) error {
ctx := context.Background()
err := d.Client.Auth.Password().SendPasswordReset(ctx, loginID, redirectUrl, nil)
@@ -197,3 +292,57 @@ func (d *DescopeProvider) UpdateUserPassword(loginID, newPassword string, r *htt
ctx := context.Background()
return d.Client.Auth.Password().UpdateUserPassword(ctx, loginID, newPassword, r)
}
func (d *DescopeProvider) resolveLoginID(loginID string) (string, error) {
if strings.Contains(loginID, "@") {
return loginID, nil
}
phone := normalizePhone(loginID)
searchOptions := &descope.UserSearchOptions{
Phones: []string{phone},
Limit: 1,
}
users, _, err := d.Client.Management.User().SearchAll(context.Background(), searchOptions)
if err != nil {
return "", fmt.Errorf("descope provider: user search failed: %w", err)
}
if len(users) == 0 {
return "", fmt.Errorf("descope provider: user not found")
}
if len(users[0].LoginIDs) > 0 {
return users[0].LoginIDs[0], nil
}
if users[0].UserID != "" {
return users[0].UserID, nil
}
return "", fmt.Errorf("descope provider: user found but login id missing")
}
func normalizePhone(phone string) string {
normalized := strings.ReplaceAll(phone, "-", "")
normalized = strings.ReplaceAll(normalized, " ", "")
if strings.HasPrefix(normalized, "010") {
return "+82" + normalized[1:]
}
if strings.HasPrefix(normalized, "82") {
return "+" + normalized
}
return normalized
}
func isDescopeNotFound(err error) bool {
if de, ok := err.(*descope.Error); ok {
if rawStatus, ok := de.Info[descope.ErrorInfoKeys.HTTPResponseStatusCode]; ok {
switch v := rawStatus.(type) {
case int:
return v == http.StatusNotFound
case float64:
return int(v) == http.StatusNotFound
case string:
return v == fmt.Sprintf("%d", http.StatusNotFound)
}
}
}
return false
}