mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-13 21:29:04 +02:00
feat: add qr code alternative sign in method (#1594)
Co-authored-by: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com>
This commit is contained in:
co-authored by
ItalyPaleAle
parent
968f97fa61
commit
e1fd1d320f
@@ -18,6 +18,11 @@ func AddDeviceTokenCookie(c *gin.Context, deviceToken string) {
|
||||
c.SetCookie(DeviceTokenCookieName, deviceToken, int(15*time.Minute.Seconds()), "/api/one-time-access-token", "", true, true)
|
||||
}
|
||||
|
||||
func AddDeviceLoginTokenCookie(c *gin.Context, requestID, deviceToken string) {
|
||||
path := "/api/device-login/requests/" + requestID + "/exchange"
|
||||
c.SetCookie(DeviceLoginTokenCookieName, deviceToken, int(15*time.Minute.Seconds()), path, "", true, true)
|
||||
}
|
||||
|
||||
func AddReauthenticationTokenCookie(c *gin.Context, reauthenticationToken string) {
|
||||
c.SetCookie(ReauthenticationTokenCookieName, reauthenticationToken, int(3*time.Minute.Seconds()), "/", "", true, true)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
var AccessTokenCookieName = "__Host-access_token"
|
||||
var SessionIdCookieName = "__Host-session"
|
||||
var DeviceTokenCookieName = "__Secure-device_token" //nolint:gosec
|
||||
var DeviceLoginTokenCookieName = "__Secure-device_login_token" //nolint:gosec
|
||||
var ReauthenticationTokenCookieName = "__Secure-reauthentication_token" //nolint:gosec
|
||||
|
||||
func init() {
|
||||
@@ -16,6 +17,7 @@ func init() {
|
||||
AccessTokenCookieName = "access_token"
|
||||
SessionIdCookieName = "session"
|
||||
DeviceTokenCookieName = "device_token"
|
||||
DeviceLoginTokenCookieName = "device_login_token"
|
||||
ReauthenticationTokenCookieName = "reauthentication_token"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,17 @@ package utils
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// CreateSha256Hash creates the SHA256 hash of a string, returning a hex-encoded string.
|
||||
func CreateSha256Hash(input string) string {
|
||||
hash := sha256.Sum256([]byte(input))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// ConstantTimeStringEqual compares two strings in constant time to prevent timing attacks.
|
||||
func ConstantTimeStringEqual(left, right string) bool {
|
||||
return subtle.ConstantTimeCompare([]byte(left), []byte(right)) == 1
|
||||
}
|
||||
|
||||
@@ -19,10 +19,21 @@ func GenerateRandomAlphanumericString(length int) (string, error) {
|
||||
|
||||
// GenerateRandomUnambiguousString generates a random string of the given length using unambiguous characters
|
||||
func GenerateRandomUnambiguousString(length int) (string, error) {
|
||||
const charset = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789"
|
||||
const charset = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ0123456789"
|
||||
return GenerateRandomString(length, charset)
|
||||
}
|
||||
|
||||
// GenerateRandomUppercaseUnambiguousString generates a random uppercase string of the given length using unambiguous characters
|
||||
func GenerateRandomUppercaseUnambiguousString(length int) (string, error) {
|
||||
const charset = "ABCDEFGHJKMNPQRSTUVWXYZ0123456789"
|
||||
return GenerateRandomString(length, charset)
|
||||
}
|
||||
|
||||
// NormalizeUnambiguousString converts commonly confused letters to the canonical digits used by generated codes
|
||||
func NormalizeUnambiguousString(value string) string {
|
||||
return strings.NewReplacer("I", "1", "i", "1", "O", "0", "o", "0").Replace(value)
|
||||
}
|
||||
|
||||
// GenerateRandomString generates a random string of the given length using the provided character set
|
||||
func GenerateRandomString(length int, charset string) (string, error) {
|
||||
|
||||
@@ -30,6 +41,10 @@ func GenerateRandomString(length int, charset string) (string, error) {
|
||||
return "", errors.New("length must be a positive integer")
|
||||
}
|
||||
|
||||
if len(charset) == 0 {
|
||||
return "", errors.New("character set must not be empty")
|
||||
}
|
||||
|
||||
// The algorithm below is adapted from https://stackoverflow.com/a/35615565
|
||||
const (
|
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestGenerateRandomUnambiguousString(t *testing.T) {
|
||||
t.Errorf("Expected length %d, got %d", length, len(str))
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(`^[abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789]+$`, str)
|
||||
matched, err := regexp.MatchString(`^[abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ0123456789]+$`, str)
|
||||
if err != nil {
|
||||
t.Errorf("Regex match failed: %v", err)
|
||||
}
|
||||
@@ -86,6 +86,26 @@ func TestGenerateRandomUnambiguousString(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateRandomUppercaseUnambiguousString(t *testing.T) {
|
||||
const length = 10
|
||||
str, err := GenerateRandomUppercaseUnambiguousString(length)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
if len(str) != length {
|
||||
t.Errorf("Expected length %d, got %d", length, len(str))
|
||||
}
|
||||
if !regexp.MustCompile(`^[ABCDEFGHJKMNPQRSTUVWXYZ0123456789]+`).MatchString(str) {
|
||||
t.Errorf("String contains lowercase or ambiguous characters: %s", str)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeUnambiguousString(t *testing.T) {
|
||||
if normalized := NormalizeUnambiguousString("iIoO-abc"); normalized != "1100-abc" {
|
||||
t.Errorf("Expected ambiguous letters to be converted, got %s", normalized)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRandomUnambiguousStringCharacterIndependence(t *testing.T) {
|
||||
const (
|
||||
sampleCount = 100_000
|
||||
@@ -112,7 +132,6 @@ func TestGenerateRandomUnambiguousStringCharacterIndependence(t *testing.T) {
|
||||
t.Errorf("first and last character collision rate = %.4f, want at most %.4f", collisionRate, maxAcceptableCollisionRate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateRandomString(t *testing.T) {
|
||||
t.Run("valid length returns characters from charset", func(t *testing.T) {
|
||||
const length = 20
|
||||
@@ -146,6 +165,14 @@ func TestGenerateRandomString(t *testing.T) {
|
||||
t.Error("Expected error for negative length, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty charset returns error", func(t *testing.T) {
|
||||
_, err := GenerateRandomString(10, "")
|
||||
if err == nil {
|
||||
t.Error("Expected error for empty charset, got nil")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestCapitalizeFirstLetter(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user