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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
# authentic_kate
Package kate provides secure cookie-based authentication for HTTP applications.
This package implements encrypted cookies with authenticated encryption.
It supports generic types to allow storage of any serializable data in encrypted HTTP cookies.
## Key features
- Type-safe authentication data storage using Go generics
- Authenticated encryption for secure cookie storage
- HTTP middlewares for required and optional authentication
- Ready-to-use login handlers for password, oauth, and magic link authentication
- Configurable cookie properties (domain, path, HTTPS-only, max age)
- Secure password hashing with industry-standard algorithms
## Example usage
```go
type UserData struct {
ID int
Name string
}
// Implement SerDes interface for UserData
type UserSerDes struct{}
func (s UserSerDes) Serialize(w io.Writer, data UserData) error { /* ... */ }
func (s UserSerDes) Deserialize(r io.Reader, data *UserData) error { /* ... */ }
// Create authentication instance
auth := kate.New("hex-encoded-32-byte-key", kate.AuthConfig[UserData]{
SerDes: UserSerDes{},
CookieName: "session",
HTTPSOnly: true,
MaxAge: 24 * time.Hour,
})
// Use as middleware
http.Handle("GET /protected", auth.Required(protectedHandler))
// Set authentication cookie
user := UserData{ID: 123, Name: "John"}
auth.Set(w, user)
// Get authenticated data
user, ok := auth.Get(r.Context())
```
## Login handlers
The package provides ready-to-use HTTP handlers for several authentication methods:
### Password-based login
```go
type User struct {
Username string
Hash string
ID int
}
// Implement PasswordUserDataStore interface
type UserStore struct{}
func (us UserStore) Fetch(username string) (User, bool, error) {
// Look up user in your database
user, exists := database.FindUser(username)
return user, exists, nil
}
func (us UserStore) GetPassHash(user User) string {
return user.Hash
}
// Configure password login
passwordConfig := kate.PasswordLoginConfig[User]{
UserData: UserStore{},
Redirects: kate.Redirects{
Default: "/dashboard",
AllowedPrefixes: []string{"/app/", "/admin/"},
FieldName: "redirect",
},
}
// Create handler
http.Handle("POST /login", auth.PasswordLoginHandler(passwordConfig))
```
### Magic link authentication
```go
// Implement MagicLinkMailer interface
type UserMailer struct{}
func (um UserMailer) Fetch(email string) (User, bool, error) {
// Look up user by email
user, exists := database.FindUserByEmail(email)
return user, exists, nil
}
func (um UserMailer) SendEmail(user User, token string) error {
// Send magic link email with the token
return emailService.SendMagicLink(user.Email, token)
}
// Configure magic link login
magicConfig := kate.MagicLinkConfig[User]{
Mailer: UserMailer{},
Redirects: kate.Redirects{ Default: "/dashboard" },
TokenExpiry: 15 * time.Minute,
TokenLocation: kate.TokenLocationQuery, // or TokenLocationPath
}
// Create handlers
http.Handle("POST /magic-link", auth.MagicLinkLoginHandler(magicConfig))
http.Handle("GET /verify", auth.MagicLinkVerifyHandler(magicConfig))
```
### OAuth2 authentication
```go
// Implement the OAuthDataStore interface
type UserDataStore struct{}
func (ds UserDataStore) GetOrCreateUser(email string) (User, error) {
// Look up user by email, create if doesn't exist
if user, exists := database.FindUserByEmail(email); exists {
return user, nil
}
return database.CreateUser(email)
}
func (ds UserDataStore) StoreState(state kate.OAuthState) (string, error) {
// Generate unique state ID and store temporarily
id := generateUniqueID()
return id, cache.Set(id, state, 10*time.Minute)
}
func (ds UserDataStore) GetAndClearState(id string) (*kate.OAuthState, error) {
// Retrieve and delete state data
var state kate.OAuthState
exists, err := cache.GetAndDelete(id, &state)
if !exists {
return nil, err
}
return &state, err
}
// Configure Google OAuth2 login
oauthConfig := kate.GoogleOAuthConfig[User](
"your-google-client-id",
"your-google-client-secret",
"https://yourapp.com/auth/callback",
UserDataStore{},
)
// Customize redirects and security settings
oauthConfig.Redirects = kate.Redirects{
Default: "/dashboard",
AllowedPrefixes: []string{"/app/", "/admin/"},
FieldName: "redirect",
}
// Create handlers
http.Handle("GET /auth/login", auth.OAuthLoginHandler(oauthConfig))
http.Handle("GET /auth/callback", auth.OAuthCallbackHandler(oauthConfig))
// GitHub OAuth2 is also supported
githubConfig := kate.GitHubOAuthConfig[User](
"your-github-client-id",
"your-github-client-secret",
"https://yourapp.com/auth/github/callback",
UserDataStore{},
)
```
## Password hashing
The package provides secure password hashing functions using Argon2id:
```go
// Hash a password with secure defaults
hash, err := kate.HashPassword("user-password", nil)
if err != nil {
// Handle error
}
// Verify a password
match, err := kate.ComparePassword("user-password", hash)
if err != nil {
// Handle malformed hash error
}
if match {
// Password is correct
}
```
## Cryptographic algorithms
This package uses the following cryptographic algorithms:
- **Token encryption**: XSalsa20 stream cipher with Poly1305 MAC for authenticated encryption (compatible with libsodium secretbox)
- **Password hashing**: Argon2id with secure default parameters and PHC format storage
|