summaryrefslogtreecommitdiff
path: root/doc.go
blob: 8d60f2a89dc493dee5e5fbfcf760ce09f305f9ab (plain)
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
// 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 middleware 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)
//   - Pluggable serialization interface for custom data formats
//   - Secure password hashing with industry-standard algorithms
//
// Example usage:
//
//	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:
//
//	type User struct {
//		Username string
//		Hash     string
//		ID       int
//	}
//
//	// Implement PasswordUserDataStore interface
//	type UserStore struct{}
//	func (us UserStore) Fetch(username string) (User, bool, error) {
//		user, exists := database.FindUser(username)
//		return user, exists, nil
//	}
//	func (us UserStore) GetPassHash(user User) string {
//		return user.Hash
//	}
//
//	// Password-based login
//	passwordConfig := kate.PasswordLoginConfig[User]{
//		UserData: UserStore{},
//		Redirects: kate.Redirects{
//			Default:         "/dashboard",
//			AllowedPrefixes: []string{"/app/", "/admin/"},
//			FieldName:       "redirect",
//		},
//	}
//	http.Handle("POST /login", auth.PasswordLoginHandler(passwordConfig))
//
//	// Implement MagicLinkMailer interface
//	type UserMailer struct{}
//	func (um UserMailer) Fetch(email string) (User, bool, error) {
//		user, exists := database.FindUserByEmail(email)
//		return user, exists, nil
//	}
//	func (um UserMailer) SendEmail(user User, token string) error {
//		return emailService.SendMagicLink(user.Email, token)
//	}
//
//	// Magic link authentication
//	magicConfig := kate.MagicLinkConfig[User]{
//		Mailer: UserMailer{},
//		Redirects: kate.Redirects{ Default: "/dashboard" },
//		TokenExpiry:     15 * time.Minute,
//		TokenLocation:   kate.TokenLocationQuery,
//	}
//	http.Handle("POST /magic-link", auth.MagicLinkLoginHandler(magicConfig))
//	http.Handle("GET /verify", auth.MagicLinkVerifyHandler(magicConfig))
//
//	// OAuth2 authentication (Google example)
//	type UserDataStore struct{}
//	func (ds UserDataStore) GetOrCreateUser(email string) (User, error) {
//		user, exists := database.FindUserByEmail(email)
//		if exists {
//			return user, nil
//		}
//		return database.CreateUser(email)
//	}
//	func (ds UserDataStore) StoreState(state kate.OAuthState) (string, error) {
//		id := generateUniqueID()
//		return id, cache.Set(id, state, 10*time.Minute)
//	}
//	func (ds UserDataStore) GetAndClearState(id string) (*kate.OAuthState, error) {
//		var state kate.OAuthState
//		exists, err := cache.GetAndDelete(id, &state)
//		if !exists {
//			return nil, err
//		}
//		return &state, err
//	}
//
//	oauthConfig := kate.GoogleOAuthConfig[User](
//		"your-client-id",
//		"your-client-secret",
//		"https://yourapp.com/auth/callback",
//		UserDataStore{},
//	)
//	// Customize redirects if needed
//	oauthConfig.Redirects = kate.Redirects{
//		Default: "/dashboard",
//		AllowedPrefixes: []string{"/app/", "/admin/"},
//		FieldName: "redirect",
//	}
//	http.Handle("GET /auth/login", auth.OAuthLoginHandler(oauthConfig))
//	http.Handle("GET /auth/callback", auth.OAuthCallbackHandler(oauthConfig))
//
// Password hashing:
//
//	// Hash a password with defaults
//	hash, err := kate.HashPassword("user-password", nil)
//
//	// 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
package kate