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
|
package auth
import (
"shrine/config"
"shrine/models"
"shrine/repositories"
"shrine/utils/crypto"
"shrine/utils/meta"
"strings"
"time"
"github.com/gofiber/fiber/v2"
)
const (
userKey = "__auth_user"
tokenHashKey = "__auth_token_hash"
)
func IsAuthenticated(context *fiber.Ctx) bool {
header, ok := meta.Request(context).Header("Authorization")
if !ok || !strings.HasPrefix(header, "Bearer ") {
return false
}
rawToken := strings.TrimPrefix(header, "Bearer ")
tokenHash := crypto.HashToken(rawToken)
token, err := repositories.FindValidToken(tokenHash)
if err != nil {
return false
}
if !token.User.CanAuthenticate() {
return false
}
now := time.Now()
if token.User.LastSeenAt == nil || time.Since(*token.User.LastSeenAt) > time.Minute {
token.User.LastSeenAt = &now
repositories.UpdateLastSeen(&token.User)
}
context.Locals(userKey, &token.User)
context.Locals(tokenHashKey, tokenHash)
return true
}
func RequireAuthentication(handler fiber.Handler) fiber.Handler {
return func(context *fiber.Ctx) error {
if !IsAuthenticated(context) {
return fiber.ErrUnauthorized
}
return handler(context)
}
}
func RequireStaff(handler fiber.Handler) fiber.Handler {
return func(context *fiber.Ctx) error {
if !IsAuthenticated(context) {
return fiber.ErrUnauthorized
}
if !GetUser(context).IsStaff() {
return fiber.ErrForbidden
}
return handler(context)
}
}
func RequireAdmin(handler fiber.Handler) fiber.Handler {
return func(context *fiber.Ctx) error {
if !IsAuthenticated(context) {
return fiber.ErrUnauthorized
}
if !GetUser(context).IsAdmin() {
return fiber.ErrForbidden
}
return handler(context)
}
}
func GetUser(context *fiber.Ctx) *models.User {
user, _ := context.Locals(userKey).(*models.User)
return user
}
func GetTokenHash(context *fiber.Ctx) string {
hash, _ := context.Locals(tokenHashKey).(string)
return hash
}
func IssueToken(context *fiber.Ctx, userID uint) (string, error) {
token, err := crypto.GenerateToken()
if err != nil {
return "", err
}
request := meta.Request(context)
userAgent, _ := request.Header("User-Agent")
record := models.Token{
TokenHash: crypto.HashToken(token),
UserID: userID,
ExpiresAt: time.Now().Add(config.Server.TokenExpiry),
IPAddress: request.IP,
UserAgent: userAgent,
}
if err := repositories.CreateToken(&record); err != nil {
return "", err
}
return token, nil
}
|