-
Notifications
You must be signed in to change notification settings - Fork 0
/
authentication_handler.go
125 lines (108 loc) · 3.89 KB
/
authentication_handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package authentication
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/golang-jwt/jwt/v5"
"github.com/jaevor/go-nanoid"
"github.com/manumura/go-auth-rbac-starter/config"
"github.com/manumura/go-auth-rbac-starter/exception"
"github.com/manumura/go-auth-rbac-starter/user"
"github.com/rs/zerolog/log"
)
type AuthenticationHandler struct {
user.UserService
AuthenticationService
config.Config
*validator.Validate
}
func NewAuthenticationHandler(userService user.UserService, authenticationService AuthenticationService, config config.Config, validate *validator.Validate) AuthenticationHandler {
return AuthenticationHandler{
userService,
authenticationService,
config,
validate,
}
}
func (h *AuthenticationHandler) Login(ctx *gin.Context) {
var req LoginRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, exception.ErrorResponse(exception.ErrInvalidRequest))
return
}
// returns nil or ValidationErrors ( []FieldError )
err := h.Validate.Struct(req)
if err != nil {
log.Error().Err(err).Msg("validation error")
ctx.AbortWithStatusJSON(http.StatusBadRequest, exception.ErrorResponse(err))
return
}
u, err := h.GetByEmail(ctx, req.Email)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, exception.ErrorResponse(exception.ErrNotFound))
return
}
if u.IsActive != 1 {
log.Error().Msg("user is not active")
ctx.AbortWithStatusJSON(http.StatusUnauthorized, exception.ErrorResponse(exception.ErrNotFound))
return
}
// Comparing the password with the hash
err = h.CheckPassword(req.Password, u.Password)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, exception.ErrorResponse(exception.ErrInvalidPassword))
return
}
now := time.Now().UTC()
userResponse := user.ToUserResponse(u)
accessToken, err := nanoid.Standard(21)
if err != nil {
log.Error().Err(err).Msg("failed to generate access token")
ctx.AbortWithStatusJSON(http.StatusInternalServerError, exception.ErrorResponse(exception.ErrInternalServer))
return
}
accessTokenAsString := accessToken()
accessTokenExpiresAt := now.Add(time.Duration(h.AccessTokenExpiresInAsSeconds) * time.Second)
refreshToken, err := nanoid.Standard(21)
if err != nil {
log.Error().Err(err).Msg("failed to generate refresh token")
ctx.AbortWithStatusJSON(http.StatusInternalServerError, exception.ErrorResponse(exception.ErrInternalServer))
return
}
refreshTokenAsString := refreshToken()
refreshTokenExpiresAt := now.Add(time.Duration(h.RefreshTokenExpiresInAsSeconds) * time.Second)
idTokenExpiresAt := now.Add(time.Duration(h.IdTokenExpiresInAsSeconds) * time.Second)
idToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iat": now.Format(time.DateTime),
"exp": idTokenExpiresAt,
"user": userResponse,
})
idTokenAsString, err := idToken.SignedString([]byte(h.JwtSecret))
if err != nil {
log.Error().Err(err).Msg("failed to create id token")
ctx.AbortWithStatusJSON(http.StatusInternalServerError, exception.ErrorResponse(exception.ErrInternalServer))
return
}
// Save the tokens in the database
authReq := AuthenticationRequest{
UserID: u.ID,
AccessToken: accessTokenAsString,
RefreshToken: refreshTokenAsString,
AccessTokenExpiresAt: accessTokenExpiresAt,
RefreshTokenExpiresAt: refreshTokenExpiresAt,
}
_, err = h.CreateAuthentication(ctx, authReq)
if err != nil {
log.Error().Err(err).Msg("failed to save authentication token")
ctx.AbortWithStatusJSON(http.StatusInternalServerError, exception.ErrorResponse(exception.ErrInternalServer))
return
}
authResponse := AuthenticationResponse{
AccessToken: accessTokenAsString,
RefreshToken: refreshTokenAsString,
IdToken: idTokenAsString,
AccessTokenExpiresAt: accessTokenExpiresAt,
}
ctx.JSON(http.StatusOK, authResponse)
}