feat: next
This commit is contained in:
parent
cdeaa13d92
commit
07cec78aa5
@ -38,6 +38,4 @@ if [ "${updated}" = true ]; then
|
||||
nix-update --flake --version=skip --subpackage client default
|
||||
git add flake.nix
|
||||
git commit -m "build(nix): updated nix hashes"
|
||||
else
|
||||
echo "nothing to update"
|
||||
fi
|
@ -21,15 +21,15 @@ apps:
|
||||
dir: server
|
||||
exts:
|
||||
- go
|
||||
onstart: go build -o ./tmp/app -tags dev && ./tmp/app
|
||||
onchange: go build -o ./tmp/app -tags dev && ./tmp/app
|
||||
onstart: go build -o ./tmp/app -tags dev cmd/trevstack/main.go && ./tmp/app
|
||||
onchange: go build -o ./tmp/app -tags dev cmd/trevstack/main.go && ./tmp/app
|
||||
|
||||
nix:
|
||||
color: "#74c7ec"
|
||||
exts:
|
||||
- nix
|
||||
onstart: nix fmt
|
||||
onchange: nix fmt
|
||||
onstart: nix fmt .
|
||||
onchange: nix fmt .
|
||||
|
||||
prettier:
|
||||
color: "#fab387"
|
||||
|
@ -5,6 +5,9 @@ managed:
|
||||
override:
|
||||
- file_option: go_package_prefix
|
||||
value: github.com/spotdemo4/trevstack/server/internal/connect
|
||||
disable:
|
||||
- file_option: go_package
|
||||
module: buf.build/bufbuild/protovalidate
|
||||
|
||||
plugins:
|
||||
- local: protoc-gen-go
|
||||
@ -18,6 +21,7 @@ plugins:
|
||||
- local: protoc-gen-es
|
||||
out: client/src/lib/connect
|
||||
opt: target=ts
|
||||
include_imports: true
|
||||
|
||||
- local: protoc-gen-connect-openapi
|
||||
out: client/static/openapi
|
||||
|
6
buf.lock
Normal file
6
buf.lock
Normal file
@ -0,0 +1,6 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v2
|
||||
deps:
|
||||
- name: buf.build/bufbuild/protovalidate
|
||||
commit: 8976f5be98c146529b1cc15cd2012b60
|
||||
digest: b5:5d513af91a439d9e78cacac0c9455c7cb885a8737d30405d0b91974fe05276d19c07a876a51a107213a3d01b83ecc912996cdad4cddf7231f91379079cf7488d
|
2
buf.yaml
2
buf.yaml
@ -2,3 +2,5 @@
|
||||
version: v2
|
||||
modules:
|
||||
- path: proto
|
||||
deps:
|
||||
- buf.build/bufbuild/protovalidate
|
||||
|
12
flake.lock
generated
12
flake.lock
generated
@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1744463964,
|
||||
"narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
|
||||
"lastModified": 1746663147,
|
||||
"narHash": "sha256-Ua0drDHawlzNqJnclTJGf87dBmaO/tn7iZ+TCkTRpRc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
|
||||
"rev": "dda3dcd3fe03e991015e9a74b22d35950f264a54",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -43,11 +43,11 @@
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744776944,
|
||||
"narHash": "sha256-wdmVDDyz6aZpFai2P0PBb2sMRBTgnaURylJw8wis6ks=",
|
||||
"lastModified": 1744951889,
|
||||
"narHash": "sha256-SXVCC/3rmTRJ4qhi63F4A9hgyGya0CTwA+EhPcow9rU=",
|
||||
"owner": "spotdemo4",
|
||||
"repo": "treli",
|
||||
"rev": "eefe10c1ba62a577592ff25e6e5b274215c1d8db",
|
||||
"rev": "6f1413a2e7324f44eb5f1dc4a2fc506c7dae4fb4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -2,11 +2,14 @@ syntax = "proto3";
|
||||
|
||||
package item.v1;
|
||||
|
||||
import "buf/validate/validate.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message Item {
|
||||
int64 id = 1;
|
||||
string name = 2;
|
||||
string name = 2 [
|
||||
(buf.validate.field).string.min_len = 3
|
||||
];
|
||||
google.protobuf.Timestamp added = 3;
|
||||
string description = 4;
|
||||
float price = 5;
|
||||
|
@ -2,17 +2,21 @@ syntax = "proto3";
|
||||
|
||||
package user.v1;
|
||||
|
||||
import "buf/validate/validate.proto";
|
||||
|
||||
service AuthService {
|
||||
rpc Login (LoginRequest) returns (LoginResponse) {}
|
||||
rpc SignUp (SignUpRequest) returns (SignUpResponse) {}
|
||||
rpc Logout (LogoutRequest) returns (LogoutResponse) {}
|
||||
// rpc GetPasskeyIDs (GetPasskeyIDsRequest) returns (GetPasskeyIDsResponse) {}
|
||||
// rpc BeginPasskeyLogin (BeginPasskeyLoginRequest) returns (BeginPasskeyLoginResponse) {}
|
||||
// rpc FinishPasskeyLogin (FinishPasskeyLoginRequest) returns (FinishPasskeyLoginResponse) {}
|
||||
|
||||
rpc BeginPasskeyLogin (BeginPasskeyLoginRequest) returns (BeginPasskeyLoginResponse) {}
|
||||
rpc FinishPasskeyLogin (FinishPasskeyLoginRequest) returns (FinishPasskeyLoginResponse) {}
|
||||
}
|
||||
|
||||
message LoginRequest {
|
||||
string username = 1;
|
||||
string username = 1 [
|
||||
(buf.validate.field).string.min_len = 3
|
||||
];
|
||||
string password = 2;
|
||||
}
|
||||
message LoginResponse {
|
||||
@ -20,7 +24,9 @@ message LoginResponse {
|
||||
}
|
||||
|
||||
message SignUpRequest {
|
||||
string username = 1;
|
||||
string username = 1 [
|
||||
(buf.validate.field).string.min_len = 3
|
||||
];
|
||||
string password = 2;
|
||||
string confirm_password = 3;
|
||||
}
|
||||
@ -29,15 +35,17 @@ message SignUpResponse {}
|
||||
message LogoutRequest {}
|
||||
message LogoutResponse {}
|
||||
|
||||
// message GetPasskeyIDsRequest {
|
||||
// string username = 1;
|
||||
// }
|
||||
// message GetPasskeyIDsResponse {
|
||||
// repeated string passkey_ids = 1;
|
||||
// }
|
||||
message BeginPasskeyLoginRequest {
|
||||
string username = 1;
|
||||
}
|
||||
message BeginPasskeyLoginResponse {
|
||||
string options_json = 1;
|
||||
}
|
||||
|
||||
// message BeginPasskeyLoginRequest {}
|
||||
// message BeginPasskeyLoginResponse {}
|
||||
|
||||
// message FinishPasskeyLoginRequest {}
|
||||
// message FinishPasskeyLoginResponse {}
|
||||
message FinishPasskeyLoginRequest {
|
||||
string username = 1;
|
||||
string attestation = 2;
|
||||
}
|
||||
message FinishPasskeyLoginResponse {
|
||||
string token = 1;
|
||||
}
|
@ -13,8 +13,9 @@ service UserService {
|
||||
rpc UpdatePassword (UpdatePasswordRequest) returns (UpdatePasswordResponse) {}
|
||||
rpc GetAPIKey (GetAPIKeyRequest) returns (GetAPIKeyResponse) {}
|
||||
rpc UpdateProfilePicture (UpdateProfilePictureRequest) returns (UpdateProfilePictureResponse) {}
|
||||
// rpc BeginPasskeyRegistration (BeginPasskeyRegistrationRequest) returns (BeginPasskeyRegistrationResponse) {}
|
||||
// rpc FinishPasskeyRegistration (FinishPasskeyRegistrationRequest) returns (FinishPasskeyRegistrationResponse) {}
|
||||
|
||||
rpc BeginPasskeyRegistration (BeginPasskeyRegistrationRequest) returns (BeginPasskeyRegistrationResponse) {}
|
||||
rpc FinishPasskeyRegistration (FinishPasskeyRegistrationRequest) returns (FinishPasskeyRegistrationResponse) {}
|
||||
}
|
||||
|
||||
message GetUserRequest {}
|
||||
@ -47,6 +48,16 @@ message UpdateProfilePictureResponse {
|
||||
User user = 1;
|
||||
}
|
||||
|
||||
message BeginPasskeyRegistrationRequest {}
|
||||
message BeginPasskeyRegistrationResponse {
|
||||
string options_json = 1;
|
||||
}
|
||||
|
||||
message FinishPasskeyRegistrationRequest {
|
||||
string attestation = 1;
|
||||
}
|
||||
message FinishPasskeyRegistrationResponse {}
|
||||
|
||||
// message BeginPasskeyRegistrationRequest {}
|
||||
// message BeginPasskeyRegistrationResponse {}
|
||||
|
||||
|
@ -8,11 +8,14 @@ import (
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"connectrpc.com/validate"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
@ -48,11 +51,27 @@ func main() {
|
||||
log.Fatalf("failed to connect to database: %s", err.Error())
|
||||
}
|
||||
|
||||
// Create webauthn
|
||||
webAuthn, err := webauthn.New(&webauthn.Config{
|
||||
RPDisplayName: env.Name,
|
||||
RPID: env.URL.Hostname(),
|
||||
RPOrigins: []string{env.URL.String()},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create webauthn: %s", err.Error())
|
||||
}
|
||||
|
||||
// Create validate interceptor
|
||||
vi, err := validate.NewInterceptor()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create validator: %s", err.Error())
|
||||
}
|
||||
|
||||
// Serve GRPC Handlers
|
||||
api := http.NewServeMux()
|
||||
api.Handle(interceptors.WithCORS(user.NewAuthHandler(sqlc, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(user.NewHandler(sqlc, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(item.NewHandler(sqlc, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(user.NewAuthHandler(vi, sqlc, webAuthn, env.Name, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(user.NewHandler(vi, sqlc, webAuthn, env.Key)))
|
||||
api.Handle(interceptors.WithCORS(item.NewHandler(vi, sqlc, env.Key)))
|
||||
|
||||
// Serve web interface
|
||||
mux := http.NewServeMux()
|
||||
@ -93,6 +112,8 @@ func main() {
|
||||
type env struct {
|
||||
Port string
|
||||
Key string
|
||||
Name string
|
||||
URL *url.URL
|
||||
DatabaseURL string
|
||||
}
|
||||
|
||||
@ -106,19 +127,36 @@ func getEnv() (*env, error) {
|
||||
env := env{
|
||||
Port: os.Getenv("PORT"),
|
||||
Key: os.Getenv("KEY"),
|
||||
Name: os.Getenv("NAME"),
|
||||
DatabaseURL: os.Getenv("DATABASE_URL"),
|
||||
}
|
||||
|
||||
// Validate
|
||||
if env.Port == "" {
|
||||
env.Port = "8080"
|
||||
log.Printf("env 'PORT' not found, defaulting to %s\n", env.Port)
|
||||
}
|
||||
if env.Key == "" {
|
||||
return nil, errors.New("env 'key' not found")
|
||||
return nil, errors.New("env 'KEY' not found")
|
||||
}
|
||||
if env.Name == "" {
|
||||
env.Name = "trevstack"
|
||||
log.Printf("env 'NAME' not found, defaulting to %s\n", env.Name)
|
||||
}
|
||||
if env.DatabaseURL == "" {
|
||||
return nil, errors.New("env 'DATABASE_URL' not found")
|
||||
}
|
||||
|
||||
// Parse URL
|
||||
if os.Getenv("URL") == "" {
|
||||
env.URL, _ = url.Parse("http://localhost:" + env.Port)
|
||||
log.Printf("env 'URL' not found, defaulting to %s\n", env.URL.String())
|
||||
} else {
|
||||
env.URL, err = url.Parse(os.Getenv("URL"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &env, nil
|
||||
}
|
||||
|
22
server/db/migrations/20250418055807_passkeys.sql
Normal file
22
server/db/migrations/20250418055807_passkeys.sql
Normal file
@ -0,0 +1,22 @@
|
||||
-- migrate:up
|
||||
CREATE TABLE credential (
|
||||
cred_id TEXT PRIMARY KEY NOT NULL,
|
||||
cred_public_key BLOB NOT NULL,
|
||||
sign_count INTEGER NOT NULL,
|
||||
transports TEXT,
|
||||
user_verified BOOLEAN,
|
||||
backup_eligible BOOLEAN,
|
||||
backup_state BOOLEAN,
|
||||
attestation_object BLOB,
|
||||
attestation_client_data BLOB,
|
||||
created_at DATETIME NOT NULL,
|
||||
last_used DATETIME NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES user (id)
|
||||
);
|
||||
ALTER TABLE user ADD webauthn_id TEXT NOT NULL;
|
||||
|
||||
-- migrate:down
|
||||
DROP TABLE credential;
|
||||
ALTER TABLE user DROP COLUMN webauthn_id;
|
83
server/db/queries/credential.sql
Normal file
83
server/db/queries/credential.sql
Normal file
@ -0,0 +1,83 @@
|
||||
-- name: GetCredential :one
|
||||
SELECT
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
FROM credential
|
||||
WHERE
|
||||
cred_id = @id
|
||||
AND
|
||||
user_id = @user_id
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetCredentials :many
|
||||
SELECT
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
FROM credential
|
||||
WHERE user_id = @user_id;
|
||||
|
||||
-- name: InsertCredential :exec
|
||||
INSERT INTO credential (
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
) VALUES (
|
||||
@cred_id,
|
||||
@cred_public_key,
|
||||
@sign_count,
|
||||
@transports,
|
||||
@user_verified,
|
||||
@backup_eligible,
|
||||
@backup_state,
|
||||
@attestation_object,
|
||||
@attestation_client_data,
|
||||
@created_at,
|
||||
@last_used,
|
||||
@user_id
|
||||
);
|
||||
|
||||
-- name: UpdateCredential :exec
|
||||
UPDATE credential
|
||||
SET
|
||||
last_used = COALESCE(sqlc.narg('last_used'), last_used),
|
||||
sign_count = COALESCE(sqlc.narg('sign_count'), sign_count)
|
||||
WHERE
|
||||
cred_id = @id
|
||||
AND
|
||||
user_id = @user_id;
|
||||
|
||||
-- name: DeleteCredential :exec
|
||||
DELETE FROM credential
|
||||
WHERE
|
||||
cred_id = @id
|
||||
AND
|
||||
user_id = @user_id;
|
@ -34,6 +34,7 @@ WHERE
|
||||
AND
|
||||
(added <= sqlc.narg('end') OR sqlc.narg('end') IS NULL)
|
||||
)
|
||||
ORDER BY added DESC
|
||||
LIMIT
|
||||
@limit
|
||||
OFFSET
|
||||
|
@ -3,7 +3,8 @@ SELECT
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
profile_picture_id
|
||||
profile_picture_id,
|
||||
webauthn_id
|
||||
FROM user
|
||||
WHERE
|
||||
id = @id
|
||||
@ -14,7 +15,8 @@ SELECT
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
profile_picture_id
|
||||
profile_picture_id,
|
||||
webauthn_id
|
||||
FROM user
|
||||
WHERE
|
||||
username = @username
|
||||
@ -23,10 +25,12 @@ LIMIT 1;
|
||||
-- name: InsertUser :one
|
||||
INSERT INTO user (
|
||||
username,
|
||||
password
|
||||
password,
|
||||
webauthn_id
|
||||
) VALUES (
|
||||
@username,
|
||||
@password
|
||||
@password,
|
||||
@webauthn_id
|
||||
)
|
||||
RETURNING id;
|
||||
|
||||
|
@ -3,7 +3,7 @@ CREATE TABLE user (
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
profile_picture_id INTEGER,
|
||||
profile_picture_id INTEGER, webauthn_id TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (profile_picture_id) REFERENCES file (id)
|
||||
);
|
||||
@ -26,6 +26,23 @@ CREATE TABLE item (
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES user (id)
|
||||
);
|
||||
CREATE TABLE credential (
|
||||
cred_id TEXT PRIMARY KEY NOT NULL,
|
||||
cred_public_key BLOB NOT NULL,
|
||||
sign_count INTEGER NOT NULL,
|
||||
transports TEXT,
|
||||
user_verified BOOLEAN,
|
||||
backup_eligible BOOLEAN,
|
||||
backup_state BOOLEAN,
|
||||
attestation_object BLOB,
|
||||
attestation_client_data BLOB,
|
||||
created_at DATETIME NOT NULL,
|
||||
last_used DATETIME NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES user (id)
|
||||
);
|
||||
-- Dbmate schema migrations
|
||||
INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20250410195416');
|
||||
('20250410195416'),
|
||||
('20250418055807');
|
||||
|
@ -3,31 +3,47 @@ module github.com/spotdemo4/trevstack/server
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1
|
||||
connectrpc.com/connect v1.18.1
|
||||
connectrpc.com/cors v0.1.0
|
||||
github.com/amacneil/dbmate/v2 v2.26.0
|
||||
connectrpc.com/validate v0.3.0
|
||||
github.com/amacneil/dbmate/v2 v2.27.0
|
||||
github.com/go-webauthn/webauthn v0.12.3
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/rs/cors v1.11.1
|
||||
github.com/spotdemo4/dbmate-sqlite-modernc v0.0.2
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/crypto v0.38.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/time v0.11.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
modernc.org/sqlite v1.37.0
|
||||
)
|
||||
|
||||
require (
|
||||
buf.build/go/protovalidate v0.12.0 // indirect
|
||||
cel.dev/expr v0.24.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-webauthn/x v0.1.21 // indirect
|
||||
github.com/google/cel-go v0.25.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-tpm v0.9.5 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
modernc.org/libc v1.62.1 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
modernc.org/libc v1.65.3 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.10.0 // indirect
|
||||
)
|
||||
|
@ -1,21 +1,42 @@
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1 h1:YhMSc48s25kr7kv31Z8vf7sPUIq5YJva9z1mn/hAt0M=
|
||||
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250425153114-8976f5be98c1.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U=
|
||||
buf.build/go/protovalidate v0.12.0 h1:4GKJotbspQjRCcqZMGVSuC8SjwZ/FmgtSuKDpKUTZew=
|
||||
buf.build/go/protovalidate v0.12.0/go.mod h1:q3PFfbzI05LeqxSwq+begW2syjy2Z6hLxZSkP1OH/D0=
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
|
||||
connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
|
||||
connectrpc.com/cors v0.1.0 h1:f3gTXJyDZPrDIZCQ567jxfD9PAIpopHiRDnJRt3QuOQ=
|
||||
connectrpc.com/cors v0.1.0/go.mod h1:v8SJZCPfHtGH1zsm+Ttajpozd4cYIUryl4dFB6QEpfg=
|
||||
connectrpc.com/validate v0.3.0 h1:eMPASBQM+ztVzuLSXddB61zwJKzvWWZ6RLdIwTgh9Wo=
|
||||
connectrpc.com/validate v0.3.0/go.mod h1:QLGN/m+oDeI4zaDAANK1L1G5K4i8gg6CUUwyl3HAG4A=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/amacneil/dbmate/v2 v2.26.0 h1:74ykEWh0V41BU3wesgmTowMn/2x9rUfIxIG+Q+vuUm0=
|
||||
github.com/amacneil/dbmate/v2 v2.26.0/go.mod h1:cnjZKm5x/gKMLPfExXbEDUUQi1Sv5UqY3AIgbnxql84=
|
||||
github.com/amacneil/dbmate/v2 v2.27.0 h1:A9JCrHD2z7bbPashxSdS17Xhfzzpu/2oB67P6j/xTVY=
|
||||
github.com/amacneil/dbmate/v2 v2.27.0/go.mod h1:3OcOFCWRyY5VhRPTGaFq6Siijgzecoe5+0A3oZbaHIc=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
|
||||
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
|
||||
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
|
||||
github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
|
||||
github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
|
||||
github.com/go-webauthn/x v0.1.21 h1:nFbckQxudvHEJn2uy1VEi713MeSpApoAv9eRqsb9AdQ=
|
||||
github.com/go-webauthn/x v0.1.21/go.mod h1:sEYohtg1zL4An1TXIUIQ5csdmoO+WO0R4R2pGKaHYKA=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
|
||||
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
|
||||
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
@ -26,8 +47,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -38,43 +61,59 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/spotdemo4/dbmate-sqlite-modernc v0.0.2 h1:537TA0HvjoWJxPzAhP+t2Gt82NpxGhCdmDsJpta7sdc=
|
||||
github.com/spotdemo4/dbmate-sqlite-modernc v0.0.2/go.mod h1:JwuvDDgb1VLGyHgSsQN3I+dx6QkEGk86o6Yc8vxzG/4=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w=
|
||||
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04/go.mod h1:FiwNQxz6hGoNFBC4nIx+CxZhI3nne5RmIOlT/MXcSD4=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 h1:IqsN8hx+lWLqlN+Sc3DoMy/watjofWiU8sRFgQ8fhKM=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
|
||||
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
|
||||
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.27.1 h1:emhLB4uoOmkZUnTDFcMI3AbkmU/Evjuerit9Taqe6Ss=
|
||||
modernc.org/ccgo/v4 v4.27.1/go.mod h1:543Q0qQhJWekKVS5P6yL5fO6liNhla9Lbm2/B3rEKDE=
|
||||
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
|
||||
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
|
||||
modernc.org/libc v1.65.3 h1:umuvKKemW1RATk14y2f0IMPYa/Bi8NB+iL+kOQYNWAw=
|
||||
modernc.org/libc v1.65.3/go.mod h1:VI3V2S5mNka4deJErQ0jsMXe7jgxojE2fOB/mWoHlbc=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4=
|
||||
|
49
server/internal/auth/creds.go
Normal file
49
server/internal/auth/creds.go
Normal file
@ -0,0 +1,49 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/spotdemo4/trevstack/server/internal/sqlc"
|
||||
)
|
||||
|
||||
func NewCreds(creds []sqlc.Credential) []webauthn.Credential {
|
||||
webauthnCreds := []webauthn.Credential{}
|
||||
|
||||
for _, c := range creds {
|
||||
transports := []protocol.AuthenticatorTransport{}
|
||||
if c.Transports != nil {
|
||||
for t := range strings.SplitSeq(*c.Transports, " ") {
|
||||
transports = append(transports, protocol.AuthenticatorTransport(t))
|
||||
}
|
||||
}
|
||||
|
||||
flags := webauthn.CredentialFlags{}
|
||||
if c.UserVerified != nil {
|
||||
flags.UserVerified = *c.UserVerified
|
||||
}
|
||||
if c.BackupEligible != nil {
|
||||
flags.BackupEligible = *c.BackupEligible
|
||||
}
|
||||
if c.BackupState != nil {
|
||||
flags.BackupState = *c.BackupState
|
||||
}
|
||||
|
||||
webauthnCreds = append(webauthnCreds, webauthn.Credential{
|
||||
ID: []byte(c.CredID),
|
||||
PublicKey: c.CredPublicKey,
|
||||
Authenticator: webauthn.Authenticator{
|
||||
SignCount: uint32(c.SignCount),
|
||||
},
|
||||
Transport: transports,
|
||||
Flags: flags,
|
||||
Attestation: webauthn.CredentialAttestation{
|
||||
Object: c.AttestationObject,
|
||||
ClientDataJSON: c.AttestationClientData,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return webauthnCreds
|
||||
}
|
33
server/internal/auth/user.go
Normal file
33
server/internal/auth/user.go
Normal file
@ -0,0 +1,33 @@
|
||||
package auth
|
||||
|
||||
import "github.com/go-webauthn/webauthn/webauthn"
|
||||
|
||||
type User struct {
|
||||
id string
|
||||
username string
|
||||
credentials []webauthn.Credential
|
||||
}
|
||||
|
||||
func NewUser(id string, username string, credentials []webauthn.Credential) User {
|
||||
return User{
|
||||
id: id,
|
||||
username: username,
|
||||
credentials: credentials,
|
||||
}
|
||||
}
|
||||
|
||||
func (u User) WebAuthnID() []byte {
|
||||
return []byte(u.id)
|
||||
}
|
||||
|
||||
func (u User) WebAuthnName() string {
|
||||
return u.username
|
||||
}
|
||||
|
||||
func (u User) WebAuthnDisplayName() string {
|
||||
return u.username
|
||||
}
|
||||
|
||||
func (u User) WebAuthnCredentials() []webauthn.Credential {
|
||||
return u.credentials
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
package itemv1
|
||||
|
||||
import (
|
||||
_ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
@ -638,10 +639,10 @@ var File_item_v1_item_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_item_v1_item_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12item/v1/item.proto\x12\aitem.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb0\x01\n" +
|
||||
"\x12item/v1/item.proto\x12\aitem.v1\x1a\x1bbuf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb9\x01\n" +
|
||||
"\x04Item\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\x120\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x1b\n" +
|
||||
"\x04name\x18\x02 \x01(\tB\a\xbaH\x04r\x02\x10\x03R\x04name\x120\n" +
|
||||
"\x05added\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x05added\x12 \n" +
|
||||
"\vdescription\x18\x04 \x01(\tR\vdescription\x12\x14\n" +
|
||||
"\x05price\x18\x05 \x01(\x02R\x05price\x12\x1a\n" +
|
||||
|
@ -7,6 +7,7 @@
|
||||
package userv1
|
||||
|
||||
import (
|
||||
_ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@ -285,27 +286,222 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
type BeginPasskeyLoginRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) Reset() {
|
||||
*x = BeginPasskeyLoginRequest{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyLoginRequest) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyLoginRequest.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyLoginRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type BeginPasskeyLoginResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
OptionsJson string `protobuf:"bytes,1,opt,name=options_json,json=optionsJson,proto3" json:"options_json,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) Reset() {
|
||||
*x = BeginPasskeyLoginResponse{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyLoginResponse) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyLoginResponse.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyLoginResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) GetOptionsJson() string {
|
||||
if x != nil {
|
||||
return x.OptionsJson
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyLoginRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Attestation string `protobuf:"bytes,2,opt,name=attestation,proto3" json:"attestation,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) Reset() {
|
||||
*x = FinishPasskeyLoginRequest{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyLoginRequest) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyLoginRequest.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyLoginRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) GetAttestation() string {
|
||||
if x != nil {
|
||||
return x.Attestation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyLoginResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) Reset() {
|
||||
*x = FinishPasskeyLoginResponse{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyLoginResponse) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyLoginResponse.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyLoginResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) GetToken() string {
|
||||
if x != nil {
|
||||
return x.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_user_v1_auth_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_user_v1_auth_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12user/v1/auth.proto\x12\auser.v1\"F\n" +
|
||||
"\fLoginRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" +
|
||||
"\x12user/v1/auth.proto\x12\auser.v1\x1a\x1bbuf/validate/validate.proto\"O\n" +
|
||||
"\fLoginRequest\x12#\n" +
|
||||
"\busername\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x03R\busername\x12\x1a\n" +
|
||||
"\bpassword\x18\x02 \x01(\tR\bpassword\"%\n" +
|
||||
"\rLoginResponse\x12\x14\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token\"r\n" +
|
||||
"\rSignUpRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token\"{\n" +
|
||||
"\rSignUpRequest\x12#\n" +
|
||||
"\busername\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x03R\busername\x12\x1a\n" +
|
||||
"\bpassword\x18\x02 \x01(\tR\bpassword\x12)\n" +
|
||||
"\x10confirm_password\x18\x03 \x01(\tR\x0fconfirmPassword\"\x10\n" +
|
||||
"\x0eSignUpResponse\"\x0f\n" +
|
||||
"\rLogoutRequest\"\x10\n" +
|
||||
"\x0eLogoutResponse2\xc1\x01\n" +
|
||||
"\x0eLogoutResponse\"6\n" +
|
||||
"\x18BeginPasskeyLoginRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\">\n" +
|
||||
"\x19BeginPasskeyLoginResponse\x12!\n" +
|
||||
"\foptions_json\x18\x01 \x01(\tR\voptionsJson\"Y\n" +
|
||||
"\x19FinishPasskeyLoginRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12 \n" +
|
||||
"\vattestation\x18\x02 \x01(\tR\vattestation\"2\n" +
|
||||
"\x1aFinishPasskeyLoginResponse\x12\x14\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token2\x80\x03\n" +
|
||||
"\vAuthService\x128\n" +
|
||||
"\x05Login\x12\x15.user.v1.LoginRequest\x1a\x16.user.v1.LoginResponse\"\x00\x12;\n" +
|
||||
"\x06SignUp\x12\x16.user.v1.SignUpRequest\x1a\x17.user.v1.SignUpResponse\"\x00\x12;\n" +
|
||||
"\x06Logout\x12\x16.user.v1.LogoutRequest\x1a\x17.user.v1.LogoutResponse\"\x00B\x9c\x01\n" +
|
||||
"\x06Logout\x12\x16.user.v1.LogoutRequest\x1a\x17.user.v1.LogoutResponse\"\x00\x12\\\n" +
|
||||
"\x11BeginPasskeyLogin\x12!.user.v1.BeginPasskeyLoginRequest\x1a\".user.v1.BeginPasskeyLoginResponse\"\x00\x12_\n" +
|
||||
"\x12FinishPasskeyLogin\x12\".user.v1.FinishPasskeyLoginRequest\x1a#.user.v1.FinishPasskeyLoginResponse\"\x00B\x9c\x01\n" +
|
||||
"\vcom.user.v1B\tAuthProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/user/v1;userv1\xa2\x02\x03UXX\xaa\x02\aUser.V1\xca\x02\aUser\\V1\xe2\x02\x13User\\V1\\GPBMetadata\xea\x02\bUser::V1b\x06proto3"
|
||||
|
||||
var (
|
||||
@ -320,24 +516,32 @@ func file_user_v1_auth_proto_rawDescGZIP() []byte {
|
||||
return file_user_v1_auth_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_user_v1_auth_proto_goTypes = []any{
|
||||
(*LoginRequest)(nil), // 0: user.v1.LoginRequest
|
||||
(*LoginResponse)(nil), // 1: user.v1.LoginResponse
|
||||
(*SignUpRequest)(nil), // 2: user.v1.SignUpRequest
|
||||
(*SignUpResponse)(nil), // 3: user.v1.SignUpResponse
|
||||
(*LogoutRequest)(nil), // 4: user.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 5: user.v1.LogoutResponse
|
||||
(*LoginRequest)(nil), // 0: user.v1.LoginRequest
|
||||
(*LoginResponse)(nil), // 1: user.v1.LoginResponse
|
||||
(*SignUpRequest)(nil), // 2: user.v1.SignUpRequest
|
||||
(*SignUpResponse)(nil), // 3: user.v1.SignUpResponse
|
||||
(*LogoutRequest)(nil), // 4: user.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 5: user.v1.LogoutResponse
|
||||
(*BeginPasskeyLoginRequest)(nil), // 6: user.v1.BeginPasskeyLoginRequest
|
||||
(*BeginPasskeyLoginResponse)(nil), // 7: user.v1.BeginPasskeyLoginResponse
|
||||
(*FinishPasskeyLoginRequest)(nil), // 8: user.v1.FinishPasskeyLoginRequest
|
||||
(*FinishPasskeyLoginResponse)(nil), // 9: user.v1.FinishPasskeyLoginResponse
|
||||
}
|
||||
var file_user_v1_auth_proto_depIdxs = []int32{
|
||||
0, // 0: user.v1.AuthService.Login:input_type -> user.v1.LoginRequest
|
||||
2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest
|
||||
4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest
|
||||
1, // 3: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse
|
||||
3, // 4: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse
|
||||
5, // 5: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse
|
||||
3, // [3:6] is the sub-list for method output_type
|
||||
0, // [0:3] is the sub-list for method input_type
|
||||
6, // 3: user.v1.AuthService.BeginPasskeyLogin:input_type -> user.v1.BeginPasskeyLoginRequest
|
||||
8, // 4: user.v1.AuthService.FinishPasskeyLogin:input_type -> user.v1.FinishPasskeyLoginRequest
|
||||
1, // 5: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse
|
||||
3, // 6: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse
|
||||
5, // 7: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse
|
||||
7, // 8: user.v1.AuthService.BeginPasskeyLogin:output_type -> user.v1.BeginPasskeyLoginResponse
|
||||
9, // 9: user.v1.AuthService.FinishPasskeyLogin:output_type -> user.v1.FinishPasskeyLoginResponse
|
||||
5, // [5:10] is the sub-list for method output_type
|
||||
0, // [0:5] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
@ -354,7 +558,7 @@ func file_user_v1_auth_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_auth_proto_rawDesc), len(file_user_v1_auth_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -457,6 +457,166 @@ func (x *UpdateProfilePictureResponse) GetUser() *User {
|
||||
return nil
|
||||
}
|
||||
|
||||
type BeginPasskeyRegistrationRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationRequest) Reset() {
|
||||
*x = BeginPasskeyRegistrationRequest{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyRegistrationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyRegistrationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyRegistrationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyRegistrationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
type BeginPasskeyRegistrationResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
OptionsJson string `protobuf:"bytes,1,opt,name=options_json,json=optionsJson,proto3" json:"options_json,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) Reset() {
|
||||
*x = BeginPasskeyRegistrationResponse{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyRegistrationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyRegistrationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyRegistrationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) GetOptionsJson() string {
|
||||
if x != nil {
|
||||
return x.OptionsJson
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyRegistrationRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Attestation string `protobuf:"bytes,1,opt,name=attestation,proto3" json:"attestation,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) Reset() {
|
||||
*x = FinishPasskeyRegistrationRequest{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyRegistrationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyRegistrationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyRegistrationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) GetAttestation() string {
|
||||
if x != nil {
|
||||
return x.Attestation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyRegistrationResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationResponse) Reset() {
|
||||
*x = FinishPasskeyRegistrationResponse{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyRegistrationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyRegistrationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyRegistrationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyRegistrationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
var File_user_v1_user_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_user_v1_user_proto_rawDesc = "" +
|
||||
@ -485,12 +645,20 @@ const file_user_v1_user_proto_rawDesc = "" +
|
||||
"\tfile_name\x18\x01 \x01(\tR\bfileName\x12\x12\n" +
|
||||
"\x04data\x18\x02 \x01(\fR\x04data\"A\n" +
|
||||
"\x1cUpdateProfilePictureResponse\x12!\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user2\xcf\x02\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user\"!\n" +
|
||||
"\x1fBeginPasskeyRegistrationRequest\"E\n" +
|
||||
" BeginPasskeyRegistrationResponse\x12!\n" +
|
||||
"\foptions_json\x18\x01 \x01(\tR\voptionsJson\"D\n" +
|
||||
" FinishPasskeyRegistrationRequest\x12 \n" +
|
||||
"\vattestation\x18\x01 \x01(\tR\vattestation\"#\n" +
|
||||
"!FinishPasskeyRegistrationResponse2\xb8\x04\n" +
|
||||
"\vUserService\x12>\n" +
|
||||
"\aGetUser\x12\x17.user.v1.GetUserRequest\x1a\x18.user.v1.GetUserResponse\"\x00\x12S\n" +
|
||||
"\x0eUpdatePassword\x12\x1e.user.v1.UpdatePasswordRequest\x1a\x1f.user.v1.UpdatePasswordResponse\"\x00\x12D\n" +
|
||||
"\tGetAPIKey\x12\x19.user.v1.GetAPIKeyRequest\x1a\x1a.user.v1.GetAPIKeyResponse\"\x00\x12e\n" +
|
||||
"\x14UpdateProfilePicture\x12$.user.v1.UpdateProfilePictureRequest\x1a%.user.v1.UpdateProfilePictureResponse\"\x00B\x9c\x01\n" +
|
||||
"\x14UpdateProfilePicture\x12$.user.v1.UpdateProfilePictureRequest\x1a%.user.v1.UpdateProfilePictureResponse\"\x00\x12q\n" +
|
||||
"\x18BeginPasskeyRegistration\x12(.user.v1.BeginPasskeyRegistrationRequest\x1a).user.v1.BeginPasskeyRegistrationResponse\"\x00\x12t\n" +
|
||||
"\x19FinishPasskeyRegistration\x12).user.v1.FinishPasskeyRegistrationRequest\x1a*.user.v1.FinishPasskeyRegistrationResponse\"\x00B\x9c\x01\n" +
|
||||
"\vcom.user.v1B\tUserProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/user/v1;userv1\xa2\x02\x03UXX\xaa\x02\aUser.V1\xca\x02\aUser\\V1\xe2\x02\x13User\\V1\\GPBMetadata\xea\x02\bUser::V1b\x06proto3"
|
||||
|
||||
var (
|
||||
@ -505,35 +673,43 @@ func file_user_v1_user_proto_rawDescGZIP() []byte {
|
||||
return file_user_v1_user_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_user_v1_user_proto_goTypes = []any{
|
||||
(*User)(nil), // 0: user.v1.User
|
||||
(*GetUserRequest)(nil), // 1: user.v1.GetUserRequest
|
||||
(*GetUserResponse)(nil), // 2: user.v1.GetUserResponse
|
||||
(*UpdatePasswordRequest)(nil), // 3: user.v1.UpdatePasswordRequest
|
||||
(*UpdatePasswordResponse)(nil), // 4: user.v1.UpdatePasswordResponse
|
||||
(*GetAPIKeyRequest)(nil), // 5: user.v1.GetAPIKeyRequest
|
||||
(*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse
|
||||
(*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest
|
||||
(*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse
|
||||
(*User)(nil), // 0: user.v1.User
|
||||
(*GetUserRequest)(nil), // 1: user.v1.GetUserRequest
|
||||
(*GetUserResponse)(nil), // 2: user.v1.GetUserResponse
|
||||
(*UpdatePasswordRequest)(nil), // 3: user.v1.UpdatePasswordRequest
|
||||
(*UpdatePasswordResponse)(nil), // 4: user.v1.UpdatePasswordResponse
|
||||
(*GetAPIKeyRequest)(nil), // 5: user.v1.GetAPIKeyRequest
|
||||
(*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse
|
||||
(*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest
|
||||
(*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse
|
||||
(*BeginPasskeyRegistrationRequest)(nil), // 9: user.v1.BeginPasskeyRegistrationRequest
|
||||
(*BeginPasskeyRegistrationResponse)(nil), // 10: user.v1.BeginPasskeyRegistrationResponse
|
||||
(*FinishPasskeyRegistrationRequest)(nil), // 11: user.v1.FinishPasskeyRegistrationRequest
|
||||
(*FinishPasskeyRegistrationResponse)(nil), // 12: user.v1.FinishPasskeyRegistrationResponse
|
||||
}
|
||||
var file_user_v1_user_proto_depIdxs = []int32{
|
||||
0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User
|
||||
0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User
|
||||
0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User
|
||||
1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest
|
||||
3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest
|
||||
5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest
|
||||
7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest
|
||||
2, // 7: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse
|
||||
4, // 8: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse
|
||||
6, // 9: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse
|
||||
8, // 10: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse
|
||||
7, // [7:11] is the sub-list for method output_type
|
||||
3, // [3:7] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User
|
||||
0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User
|
||||
0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User
|
||||
1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest
|
||||
3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest
|
||||
5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest
|
||||
7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest
|
||||
9, // 7: user.v1.UserService.BeginPasskeyRegistration:input_type -> user.v1.BeginPasskeyRegistrationRequest
|
||||
11, // 8: user.v1.UserService.FinishPasskeyRegistration:input_type -> user.v1.FinishPasskeyRegistrationRequest
|
||||
2, // 9: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse
|
||||
4, // 10: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse
|
||||
6, // 11: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse
|
||||
8, // 12: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse
|
||||
10, // 13: user.v1.UserService.BeginPasskeyRegistration:output_type -> user.v1.BeginPasskeyRegistrationResponse
|
||||
12, // 14: user.v1.UserService.FinishPasskeyRegistration:output_type -> user.v1.FinishPasskeyRegistrationResponse
|
||||
9, // [9:15] is the sub-list for method output_type
|
||||
3, // [3:9] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_user_v1_user_proto_init() }
|
||||
@ -548,7 +724,7 @@ func file_user_v1_user_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_user_proto_rawDesc), len(file_user_v1_user_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 9,
|
||||
NumMessages: 13,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -39,6 +39,12 @@ const (
|
||||
AuthServiceSignUpProcedure = "/user.v1.AuthService/SignUp"
|
||||
// AuthServiceLogoutProcedure is the fully-qualified name of the AuthService's Logout RPC.
|
||||
AuthServiceLogoutProcedure = "/user.v1.AuthService/Logout"
|
||||
// AuthServiceBeginPasskeyLoginProcedure is the fully-qualified name of the AuthService's
|
||||
// BeginPasskeyLogin RPC.
|
||||
AuthServiceBeginPasskeyLoginProcedure = "/user.v1.AuthService/BeginPasskeyLogin"
|
||||
// AuthServiceFinishPasskeyLoginProcedure is the fully-qualified name of the AuthService's
|
||||
// FinishPasskeyLogin RPC.
|
||||
AuthServiceFinishPasskeyLoginProcedure = "/user.v1.AuthService/FinishPasskeyLogin"
|
||||
)
|
||||
|
||||
// AuthServiceClient is a client for the user.v1.AuthService service.
|
||||
@ -46,6 +52,8 @@ type AuthServiceClient interface {
|
||||
Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error)
|
||||
SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error)
|
||||
Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error)
|
||||
BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error)
|
||||
FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error)
|
||||
}
|
||||
|
||||
// NewAuthServiceClient constructs a client for the user.v1.AuthService service. By default, it uses
|
||||
@ -77,14 +85,28 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
||||
connect.WithSchema(authServiceMethods.ByName("Logout")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
beginPasskeyLogin: connect.NewClient[v1.BeginPasskeyLoginRequest, v1.BeginPasskeyLoginResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceBeginPasskeyLoginProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("BeginPasskeyLogin")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
finishPasskeyLogin: connect.NewClient[v1.FinishPasskeyLoginRequest, v1.FinishPasskeyLoginResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceFinishPasskeyLoginProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("FinishPasskeyLogin")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// authServiceClient implements AuthServiceClient.
|
||||
type authServiceClient struct {
|
||||
login *connect.Client[v1.LoginRequest, v1.LoginResponse]
|
||||
signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse]
|
||||
logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse]
|
||||
login *connect.Client[v1.LoginRequest, v1.LoginResponse]
|
||||
signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse]
|
||||
logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse]
|
||||
beginPasskeyLogin *connect.Client[v1.BeginPasskeyLoginRequest, v1.BeginPasskeyLoginResponse]
|
||||
finishPasskeyLogin *connect.Client[v1.FinishPasskeyLoginRequest, v1.FinishPasskeyLoginResponse]
|
||||
}
|
||||
|
||||
// Login calls user.v1.AuthService.Login.
|
||||
@ -102,11 +124,23 @@ func (c *authServiceClient) Logout(ctx context.Context, req *connect.Request[v1.
|
||||
return c.logout.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// BeginPasskeyLogin calls user.v1.AuthService.BeginPasskeyLogin.
|
||||
func (c *authServiceClient) BeginPasskeyLogin(ctx context.Context, req *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) {
|
||||
return c.beginPasskeyLogin.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// FinishPasskeyLogin calls user.v1.AuthService.FinishPasskeyLogin.
|
||||
func (c *authServiceClient) FinishPasskeyLogin(ctx context.Context, req *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) {
|
||||
return c.finishPasskeyLogin.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// AuthServiceHandler is an implementation of the user.v1.AuthService service.
|
||||
type AuthServiceHandler interface {
|
||||
Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error)
|
||||
SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error)
|
||||
Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error)
|
||||
BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error)
|
||||
FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error)
|
||||
}
|
||||
|
||||
// NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path
|
||||
@ -134,6 +168,18 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption
|
||||
connect.WithSchema(authServiceMethods.ByName("Logout")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceBeginPasskeyLoginHandler := connect.NewUnaryHandler(
|
||||
AuthServiceBeginPasskeyLoginProcedure,
|
||||
svc.BeginPasskeyLogin,
|
||||
connect.WithSchema(authServiceMethods.ByName("BeginPasskeyLogin")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceFinishPasskeyLoginHandler := connect.NewUnaryHandler(
|
||||
AuthServiceFinishPasskeyLoginProcedure,
|
||||
svc.FinishPasskeyLogin,
|
||||
connect.WithSchema(authServiceMethods.ByName("FinishPasskeyLogin")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
return "/user.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case AuthServiceLoginProcedure:
|
||||
@ -142,6 +188,10 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption
|
||||
authServiceSignUpHandler.ServeHTTP(w, r)
|
||||
case AuthServiceLogoutProcedure:
|
||||
authServiceLogoutHandler.ServeHTTP(w, r)
|
||||
case AuthServiceBeginPasskeyLoginProcedure:
|
||||
authServiceBeginPasskeyLoginHandler.ServeHTTP(w, r)
|
||||
case AuthServiceFinishPasskeyLoginProcedure:
|
||||
authServiceFinishPasskeyLoginHandler.ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@ -162,3 +212,11 @@ func (UnimplementedAuthServiceHandler) SignUp(context.Context, *connect.Request[
|
||||
func (UnimplementedAuthServiceHandler) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.Logout is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.BeginPasskeyLogin is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.FinishPasskeyLogin is not implemented"))
|
||||
}
|
||||
|
@ -43,6 +43,12 @@ const (
|
||||
// UserServiceUpdateProfilePictureProcedure is the fully-qualified name of the UserService's
|
||||
// UpdateProfilePicture RPC.
|
||||
UserServiceUpdateProfilePictureProcedure = "/user.v1.UserService/UpdateProfilePicture"
|
||||
// UserServiceBeginPasskeyRegistrationProcedure is the fully-qualified name of the UserService's
|
||||
// BeginPasskeyRegistration RPC.
|
||||
UserServiceBeginPasskeyRegistrationProcedure = "/user.v1.UserService/BeginPasskeyRegistration"
|
||||
// UserServiceFinishPasskeyRegistrationProcedure is the fully-qualified name of the UserService's
|
||||
// FinishPasskeyRegistration RPC.
|
||||
UserServiceFinishPasskeyRegistrationProcedure = "/user.v1.UserService/FinishPasskeyRegistration"
|
||||
)
|
||||
|
||||
// UserServiceClient is a client for the user.v1.UserService service.
|
||||
@ -51,6 +57,8 @@ type UserServiceClient interface {
|
||||
UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error)
|
||||
GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error)
|
||||
UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error)
|
||||
BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error)
|
||||
FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error)
|
||||
}
|
||||
|
||||
// NewUserServiceClient constructs a client for the user.v1.UserService service. By default, it uses
|
||||
@ -88,15 +96,29 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
||||
connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
beginPasskeyRegistration: connect.NewClient[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceBeginPasskeyRegistrationProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("BeginPasskeyRegistration")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
finishPasskeyRegistration: connect.NewClient[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceFinishPasskeyRegistrationProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("FinishPasskeyRegistration")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// userServiceClient implements UserServiceClient.
|
||||
type userServiceClient struct {
|
||||
getUser *connect.Client[v1.GetUserRequest, v1.GetUserResponse]
|
||||
updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse]
|
||||
getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse]
|
||||
updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse]
|
||||
getUser *connect.Client[v1.GetUserRequest, v1.GetUserResponse]
|
||||
updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse]
|
||||
getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse]
|
||||
updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse]
|
||||
beginPasskeyRegistration *connect.Client[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse]
|
||||
finishPasskeyRegistration *connect.Client[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse]
|
||||
}
|
||||
|
||||
// GetUser calls user.v1.UserService.GetUser.
|
||||
@ -119,12 +141,24 @@ func (c *userServiceClient) UpdateProfilePicture(ctx context.Context, req *conne
|
||||
return c.updateProfilePicture.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// BeginPasskeyRegistration calls user.v1.UserService.BeginPasskeyRegistration.
|
||||
func (c *userServiceClient) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) {
|
||||
return c.beginPasskeyRegistration.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// FinishPasskeyRegistration calls user.v1.UserService.FinishPasskeyRegistration.
|
||||
func (c *userServiceClient) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) {
|
||||
return c.finishPasskeyRegistration.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// UserServiceHandler is an implementation of the user.v1.UserService service.
|
||||
type UserServiceHandler interface {
|
||||
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.GetUserResponse], error)
|
||||
UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error)
|
||||
GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error)
|
||||
UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error)
|
||||
BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error)
|
||||
FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error)
|
||||
}
|
||||
|
||||
// NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path
|
||||
@ -158,6 +192,18 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
||||
connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceBeginPasskeyRegistrationHandler := connect.NewUnaryHandler(
|
||||
UserServiceBeginPasskeyRegistrationProcedure,
|
||||
svc.BeginPasskeyRegistration,
|
||||
connect.WithSchema(userServiceMethods.ByName("BeginPasskeyRegistration")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceFinishPasskeyRegistrationHandler := connect.NewUnaryHandler(
|
||||
UserServiceFinishPasskeyRegistrationProcedure,
|
||||
svc.FinishPasskeyRegistration,
|
||||
connect.WithSchema(userServiceMethods.ByName("FinishPasskeyRegistration")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
return "/user.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case UserServiceGetUserProcedure:
|
||||
@ -168,6 +214,10 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
||||
userServiceGetAPIKeyHandler.ServeHTTP(w, r)
|
||||
case UserServiceUpdateProfilePictureProcedure:
|
||||
userServiceUpdateProfilePictureHandler.ServeHTTP(w, r)
|
||||
case UserServiceBeginPasskeyRegistrationProcedure:
|
||||
userServiceBeginPasskeyRegistrationHandler.ServeHTTP(w, r)
|
||||
case UserServiceFinishPasskeyRegistrationProcedure:
|
||||
userServiceFinishPasskeyRegistrationHandler.ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@ -192,3 +242,11 @@ func (UnimplementedUserServiceHandler) GetAPIKey(context.Context, *connect.Reque
|
||||
func (UnimplementedUserServiceHandler) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.UpdateProfilePicture is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.BeginPasskeyRegistration is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.FinishPasskeyRegistration is not implemented"))
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package database
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
@ -11,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
func Migrate(dsn string, dbFS embed.FS) error {
|
||||
_, err := dbFS.ReadDir(".")
|
||||
if err == io.EOF {
|
||||
entries, err := dbFS.ReadDir(".")
|
||||
if err != nil || len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -35,7 +34,7 @@ func Migrate(dsn string, dbFS embed.FS) error {
|
||||
log.Println(m.Version, m.FilePath)
|
||||
}
|
||||
|
||||
log.Println("\nApplying...")
|
||||
log.Println("Applying...")
|
||||
err = db.CreateAndMigrate()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2,7 +2,6 @@ package client
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
@ -10,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
func NewClientHandler(key string, clientFS embed.FS) http.Handler {
|
||||
_, err := clientFS.ReadDir(".")
|
||||
if err == io.EOF {
|
||||
entries, err := clientFS.ReadDir(".")
|
||||
if err != nil || len(entries) == 0 {
|
||||
return http.NotFoundHandler()
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/validate"
|
||||
itemv1 "github.com/spotdemo4/trevstack/server/internal/connect/item/v1"
|
||||
"github.com/spotdemo4/trevstack/server/internal/connect/item/v1/itemv1connect"
|
||||
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
||||
@ -190,8 +191,8 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NewHandler(db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
|
||||
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(vi, interceptors.NewAuthInterceptor(key))
|
||||
|
||||
return itemv1connect.NewItemServiceHandler(
|
||||
&Handler{
|
||||
|
@ -3,15 +3,20 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "crypto/sha256" // Crypto
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/validate"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spotdemo4/trevstack/server/internal/auth"
|
||||
userv1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1"
|
||||
"github.com/spotdemo4/trevstack/server/internal/connect/user/v1/userv1connect"
|
||||
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
||||
@ -20,8 +25,13 @@ import (
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
db *sqlc.Queries
|
||||
key []byte
|
||||
db *sqlc.Queries
|
||||
webAuthn *webauthn.WebAuthn
|
||||
key []byte
|
||||
name string
|
||||
|
||||
sessions *map[string]*webauthn.SessionData
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) {
|
||||
@ -40,37 +50,18 @@ func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.Log
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("invalid username or password"))
|
||||
}
|
||||
|
||||
// Generate JWT
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
Issuer: "trevstack",
|
||||
Subject: strconv.FormatInt(user.ID, 10),
|
||||
IssuedAt: &jwt.NumericDate{
|
||||
Time: time.Now(),
|
||||
},
|
||||
ExpiresAt: &jwt.NumericDate{
|
||||
Time: time.Now().Add(time.Hour * 24),
|
||||
},
|
||||
})
|
||||
ss, err := t.SignedString(h.key)
|
||||
// Create JWT
|
||||
token, cookie, err := h.createJWT(user.ID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// Create cookie
|
||||
cookie := http.Cookie{
|
||||
Name: "token",
|
||||
Value: ss,
|
||||
Path: "/",
|
||||
MaxAge: 86400,
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
}
|
||||
|
||||
// Create response
|
||||
res := connect.NewResponse(&userv1.LoginResponse{
|
||||
Token: ss,
|
||||
Token: token,
|
||||
})
|
||||
res.Header().Set("Set-Cookie", cookie.String())
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@ -82,7 +73,7 @@ func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.Si
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeAlreadyExists, err)
|
||||
return nil, connect.NewError(connect.CodeAlreadyExists, errors.New("user already exists"))
|
||||
}
|
||||
|
||||
// Check if confirmation passwords match
|
||||
@ -98,8 +89,9 @@ func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.Si
|
||||
|
||||
// Create user
|
||||
_, err = h.db.InsertUser(ctx, sqlc.InsertUserParams{
|
||||
Username: req.Msg.Username,
|
||||
Password: string(hash),
|
||||
Username: req.Msg.Username,
|
||||
Password: string(hash),
|
||||
WebauthnID: uuid.New().String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
@ -123,104 +115,173 @@ func (h *AuthHandler) Logout(_ context.Context, _ *connect.Request[userv1.Logout
|
||||
|
||||
res := connect.NewResponse(&userv1.LogoutResponse{})
|
||||
res.Header().Set("Set-Cookie", cookie.String())
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// func (h *AuthHandler) GetPasskeyIDs(_ context.Context, req *connect.Request[userv1.GetPasskeyIDsRequest]) (*connect.Response[userv1.GetPasskeyIDsResponse], error) {
|
||||
// // Get user
|
||||
// user := models.User{}
|
||||
// if err := h.db.Preload("Passkeys").First(&user, "username = ?", req.Msg.Username).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeNotFound, err)
|
||||
// }
|
||||
func (h *AuthHandler) BeginPasskeyLogin(ctx context.Context, req *connect.Request[userv1.BeginPasskeyLoginRequest]) (*connect.Response[userv1.BeginPasskeyLoginResponse], error) {
|
||||
// Get user
|
||||
pUser, _, err := h.getPasskeyUser(ctx, req.Msg.Username)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, err)
|
||||
}
|
||||
|
||||
// // Get IDs
|
||||
// ids := []string{}
|
||||
// for _, passkey := range user.Passkeys {
|
||||
// ids = append(ids, passkey.ID)
|
||||
// }
|
||||
// Get options for user
|
||||
options, session, err := h.webAuthn.BeginLogin(pUser)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// return connect.NewResponse(&userv1.GetPasskeyIDsResponse{
|
||||
// PasskeyIds: ids,
|
||||
// }), nil
|
||||
// }
|
||||
// Turn the options into json
|
||||
optionsJSON, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// func (h *AuthHandler) PasskeyLogin(_ context.Context, req *connect.Request[userv1.PasskeyLoginRequest]) (*connect.Response[userv1.PasskeyLoginResponse], error) {
|
||||
// // Get passkey
|
||||
// passkey := models.Passkey{}
|
||||
// if err := h.db.First(&passkey, "id = ?", req.Msg.Id).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeNotFound, err)
|
||||
// }
|
||||
// Set session for validation later
|
||||
h.setSession(req.Msg.Username, session)
|
||||
|
||||
// // create a verifier from a trusted private key
|
||||
// var verifier cose.Verifier
|
||||
// var err error
|
||||
// switch req.Msg.Algorithm {
|
||||
// case -7:
|
||||
// verifier, err = cose.NewVerifier(cose.AlgorithmES256, passkey.PublicKey)
|
||||
return connect.NewResponse(&userv1.BeginPasskeyLoginResponse{
|
||||
OptionsJson: string(optionsJSON),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// case -257:
|
||||
// verifier, err = cose.NewVerifier(cose.AlgorithmRS256, passkey.PublicKey)
|
||||
func (h *AuthHandler) FinishPasskeyLogin(ctx context.Context, req *connect.Request[userv1.FinishPasskeyLoginRequest]) (*connect.Response[userv1.FinishPasskeyLoginResponse], error) {
|
||||
// Get user
|
||||
pUser, userID, err := h.getPasskeyUser(ctx, req.Msg.Username)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// default:
|
||||
// return nil, connect.NewError(connect.CodeInternal, errors.New("decode algorithm not implemented"))
|
||||
// }
|
||||
// if err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Get the session data previously set
|
||||
session, err := h.getSession(req.Msg.Username)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// // create a sign message from a raw signature payload
|
||||
// var msg cose.Sign1Message
|
||||
// if err = msg.UnmarshalCBOR(req.Msg.Signature); err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Parse the attestation response
|
||||
parsedResponse, err := protocol.ParseCredentialRequestResponseBytes([]byte(req.Msg.Attestation))
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// // Validate passkey
|
||||
// err = msg.Verify(nil, verifier)
|
||||
// if err != nil {
|
||||
// return nil, connect.NewError(connect.CodeUnauthenticated, err)
|
||||
// }
|
||||
// Validate the login
|
||||
credential, err := h.webAuthn.ValidateLogin(pUser, *session, parsedResponse)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, err)
|
||||
}
|
||||
|
||||
// // Generate JWT
|
||||
// t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
// Issuer: "trevstack",
|
||||
// Subject: strconv.FormatUint(uint64(passkey.UserID), 10),
|
||||
// IssuedAt: &jwt.NumericDate{
|
||||
// Time: time.Now(),
|
||||
// },
|
||||
// ExpiresAt: &jwt.NumericDate{
|
||||
// Time: time.Now().Add(time.Hour * 24),
|
||||
// },
|
||||
// })
|
||||
// ss, err := t.SignedString(h.key)
|
||||
// if err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Update the credential in the database
|
||||
lastUsed := time.Now()
|
||||
signCount := int64(credential.Authenticator.SignCount)
|
||||
err = h.db.UpdateCredential(ctx, sqlc.UpdateCredentialParams{
|
||||
// set
|
||||
LastUsed: &lastUsed,
|
||||
SignCount: &signCount,
|
||||
|
||||
// // Create cookie
|
||||
// cookie := http.Cookie{
|
||||
// Name: "token",
|
||||
// Value: ss,
|
||||
// Path: "/",
|
||||
// MaxAge: 86400,
|
||||
// HttpOnly: true,
|
||||
// Secure: true,
|
||||
// SameSite: http.SameSiteStrictMode,
|
||||
// }
|
||||
// where
|
||||
ID: string(credential.ID),
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// res := connect.NewResponse(&userv1.PasskeyLoginResponse{
|
||||
// Token: ss,
|
||||
// })
|
||||
// res.Header().Set("Set-Cookie", cookie.String())
|
||||
// return res, nil
|
||||
// }
|
||||
// Create JWT
|
||||
token, cookie, err := h.createJWT(userID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
func NewAuthHandler(db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewRateLimitInterceptor(key))
|
||||
// Create response
|
||||
resp := connect.NewResponse(&userv1.FinishPasskeyLoginResponse{
|
||||
Token: token,
|
||||
})
|
||||
resp.Header().Set("Set-Cookie", cookie.String())
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) getPasskeyUser(ctx context.Context, username string) (*auth.User, int64, error) {
|
||||
user, err := h.db.GetUserbyUsername(ctx, username)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
creds, err := h.db.GetCredentials(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
webCreds := auth.NewCreds(creds)
|
||||
webUser := auth.NewUser(user.WebauthnID, user.Username, webCreds)
|
||||
|
||||
return &webUser, user.ID, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) getSession(username string) (*webauthn.SessionData, error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
session, ok := (*h.sessions)[username]
|
||||
if !ok {
|
||||
return nil, errors.New("session does not exist")
|
||||
}
|
||||
|
||||
delete(*h.sessions, username)
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) setSession(username string, data *webauthn.SessionData) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
(*h.sessions)[username] = data
|
||||
}
|
||||
|
||||
func (h *AuthHandler) createJWT(userid int64) (string, *http.Cookie, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
Issuer: h.name,
|
||||
Subject: strconv.Itoa(int(userid)),
|
||||
IssuedAt: &jwt.NumericDate{
|
||||
Time: time.Now(),
|
||||
},
|
||||
ExpiresAt: &jwt.NumericDate{
|
||||
Time: time.Now().Add(time.Hour * 24),
|
||||
},
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString(h.key)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
cookie := http.Cookie{
|
||||
Name: "token",
|
||||
Value: tokenString,
|
||||
Path: "/",
|
||||
MaxAge: 86400,
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
}
|
||||
|
||||
return tokenString, &cookie, nil
|
||||
}
|
||||
|
||||
func NewAuthHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, name string, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(vi, interceptors.NewRateLimitInterceptor(key))
|
||||
|
||||
sd := map[string]*webauthn.SessionData{}
|
||||
return userv1connect.NewAuthServiceHandler(
|
||||
&AuthHandler{
|
||||
db: db,
|
||||
key: []byte(key),
|
||||
db: db,
|
||||
webAuthn: webauth,
|
||||
name: name,
|
||||
key: []byte(key),
|
||||
|
||||
sessions: &sd,
|
||||
mu: sync.Mutex{},
|
||||
},
|
||||
interceptors,
|
||||
)
|
||||
|
@ -3,13 +3,19 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/validate"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/spotdemo4/trevstack/server/internal/auth"
|
||||
userv1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1"
|
||||
"github.com/spotdemo4/trevstack/server/internal/connect/user/v1/userv1connect"
|
||||
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
||||
@ -27,8 +33,12 @@ func userToConnect(item sqlc.User) *userv1.User {
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
db *sqlc.Queries
|
||||
key []byte
|
||||
db *sqlc.Queries
|
||||
webAuthn *webauthn.WebAuthn
|
||||
key []byte
|
||||
|
||||
sessions *map[int64]*webauthn.SessionData
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (h *Handler) GetUser(ctx context.Context, _ *connect.Request[userv1.GetUserRequest]) (*connect.Response[userv1.GetUserResponse], error) {
|
||||
@ -200,75 +210,149 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// func (h *Handler) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) {
|
||||
// // Get user ID from context
|
||||
// userid, ok := interceptors.GetUserContext(ctx)
|
||||
// if !ok {
|
||||
// return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated"))
|
||||
// }
|
||||
func (h *Handler) BeginPasskeyRegistration(ctx context.Context, _ *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) {
|
||||
userid, ok := interceptors.GetUserContext(ctx)
|
||||
if !ok {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
|
||||
}
|
||||
|
||||
// // Get user
|
||||
// user := models.User{}
|
||||
// if err := h.db.First(&user, "id = ?", userid).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Get user
|
||||
pUser, err := h.getPasskeyUser(ctx, userid)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// return connect.NewResponse(&userv1.BeginPasskeyRegistrationResponse{}), nil
|
||||
// }
|
||||
// Get options for user
|
||||
options, session, err := h.webAuthn.BeginRegistration(pUser)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) {
|
||||
// // Get user ID from context
|
||||
// userid, ok := interceptors.GetUserContext(ctx)
|
||||
// if !ok {
|
||||
// return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated"))
|
||||
// }
|
||||
// Turn options into json
|
||||
optionsJSON, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// // Get user
|
||||
// user := models.User{}
|
||||
// if err := h.db.First(&user, "id = ?", userid).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Set session for validation later
|
||||
h.setSession(userid, session)
|
||||
|
||||
// return connect.NewResponse(&userv1.FinishPasskeyRegistrationResponse{}), nil
|
||||
// }
|
||||
return connect.NewResponse(&userv1.BeginPasskeyRegistrationResponse{
|
||||
OptionsJson: string(optionsJSON),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// func BeginRegistration(ctx context.Context) error {
|
||||
// userid, ok := interceptors.GetUserContext(ctx)
|
||||
// if !ok {
|
||||
// return nil
|
||||
// }
|
||||
func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) {
|
||||
userid, ok := interceptors.GetUserContext(ctx)
|
||||
if !ok {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
|
||||
}
|
||||
|
||||
// wconfig := &webauthn.Config{
|
||||
// RPDisplayName: "Go Webauthn", // Display Name for your site
|
||||
// RPID: "go-webauthn.local", // Generally the FQDN for your site
|
||||
// RPOrigins: []string{"https://login.go-webauthn.local"}, // The origin URLs allowed for WebAuthn requests
|
||||
// }
|
||||
// webAuthn, err := webauthn.New(wconfig)
|
||||
// if err != nil {
|
||||
// return nil
|
||||
// }
|
||||
// Get user
|
||||
pUser, err := h.getPasskeyUser(ctx, userid)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// var user webauthn.User
|
||||
// user.WebAuthnCredentials()
|
||||
// Get the session data previously set
|
||||
session, err := h.getSession(userid)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// var cred webauthn.Credential
|
||||
// cred.Verify()
|
||||
// Parse the attestation response
|
||||
parsedResponse, err := protocol.ParseCredentialCreationResponseBytes([]byte(req.Msg.Attestation))
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// var test metadata.Provider
|
||||
// test.
|
||||
// Create the credential
|
||||
credential, err := h.webAuthn.CreateCredential(pUser, *session, parsedResponse)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// options, session, err := webAuthn.BeginRegistration(user)
|
||||
transports := transportsToString(credential.Transport)
|
||||
|
||||
// return nil
|
||||
// }
|
||||
// Save the credential
|
||||
err = h.db.InsertCredential(ctx, sqlc.InsertCredentialParams{
|
||||
CredID: string(credential.ID),
|
||||
CredPublicKey: credential.PublicKey,
|
||||
SignCount: int64(credential.Authenticator.SignCount),
|
||||
Transports: &transports,
|
||||
UserVerified: &credential.Flags.UserVerified,
|
||||
BackupEligible: &credential.Flags.BackupEligible,
|
||||
BackupState: &credential.Flags.BackupState,
|
||||
AttestationObject: credential.Attestation.Object,
|
||||
AttestationClientData: credential.Attestation.ClientDataJSON,
|
||||
CreatedAt: time.Now(),
|
||||
LastUsed: time.Now(),
|
||||
UserID: userid,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
func NewHandler(db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
|
||||
return connect.NewResponse(&userv1.FinishPasskeyRegistrationResponse{}), nil
|
||||
}
|
||||
|
||||
func (h *Handler) getPasskeyUser(ctx context.Context, userid int64) (*auth.User, error) {
|
||||
user, err := h.db.GetUser(ctx, userid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
creds, err := h.db.GetCredentials(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
webCreds := auth.NewCreds(creds)
|
||||
webUser := auth.NewUser(user.WebauthnID, user.Username, webCreds)
|
||||
|
||||
return &webUser, nil
|
||||
}
|
||||
|
||||
func (h *Handler) getSession(userid int64) (*webauthn.SessionData, error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
session, ok := (*h.sessions)[userid]
|
||||
if !ok {
|
||||
return nil, errors.New("session does not exist")
|
||||
}
|
||||
|
||||
delete(*h.sessions, userid)
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (h *Handler) setSession(userid int64, data *webauthn.SessionData) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
(*h.sessions)[userid] = data
|
||||
}
|
||||
|
||||
func transportsToString(transports []protocol.AuthenticatorTransport) string {
|
||||
s := ""
|
||||
for _, transport := range transports {
|
||||
s += string(transport) + ", "
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key), vi)
|
||||
|
||||
sd := map[int64]*webauthn.SessionData{}
|
||||
return userv1connect.NewUserServiceHandler(
|
||||
&Handler{
|
||||
db: db,
|
||||
key: []byte(key),
|
||||
db: db,
|
||||
webAuthn: webauth,
|
||||
key: []byte(key),
|
||||
|
||||
sessions: &sd,
|
||||
mu: sync.Mutex{},
|
||||
},
|
||||
interceptors,
|
||||
)
|
||||
|
@ -96,7 +96,13 @@ func (i *AuthInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
|
||||
ctx, err = newUserContext(ctx, subject)
|
||||
if err == nil {
|
||||
return next(ctx, req)
|
||||
} else {
|
||||
log.Println("huh")
|
||||
log.Println(err)
|
||||
}
|
||||
} else {
|
||||
log.Println("what")
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,7 +182,7 @@ func getCookies(rawCookies string) []*http.Cookie {
|
||||
}
|
||||
|
||||
func validateToken(tokenString string, key string) (subject string, err error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
|
221
server/internal/sqlc/credential.sql.go
Normal file
221
server/internal/sqlc/credential.sql.go
Normal file
@ -0,0 +1,221 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// source: credential.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const deleteCredential = `-- name: DeleteCredential :exec
|
||||
DELETE FROM credential
|
||||
WHERE
|
||||
cred_id = ?1
|
||||
AND
|
||||
user_id = ?2
|
||||
`
|
||||
|
||||
type DeleteCredentialParams struct {
|
||||
ID string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCredential(ctx context.Context, arg DeleteCredentialParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCredential, arg.ID, arg.UserID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCredential = `-- name: GetCredential :one
|
||||
SELECT
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
FROM credential
|
||||
WHERE
|
||||
cred_id = ?1
|
||||
AND
|
||||
user_id = ?2
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetCredentialParams struct {
|
||||
ID string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) GetCredential(ctx context.Context, arg GetCredentialParams) (Credential, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCredential, arg.ID, arg.UserID)
|
||||
var i Credential
|
||||
err := row.Scan(
|
||||
&i.CredID,
|
||||
&i.CredPublicKey,
|
||||
&i.SignCount,
|
||||
&i.Transports,
|
||||
&i.UserVerified,
|
||||
&i.BackupEligible,
|
||||
&i.BackupState,
|
||||
&i.AttestationObject,
|
||||
&i.AttestationClientData,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsed,
|
||||
&i.UserID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCredentials = `-- name: GetCredentials :many
|
||||
SELECT
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
FROM credential
|
||||
WHERE user_id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCredentials(ctx context.Context, userID int64) ([]Credential, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCredentials, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Credential
|
||||
for rows.Next() {
|
||||
var i Credential
|
||||
if err := rows.Scan(
|
||||
&i.CredID,
|
||||
&i.CredPublicKey,
|
||||
&i.SignCount,
|
||||
&i.Transports,
|
||||
&i.UserVerified,
|
||||
&i.BackupEligible,
|
||||
&i.BackupState,
|
||||
&i.AttestationObject,
|
||||
&i.AttestationClientData,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsed,
|
||||
&i.UserID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertCredential = `-- name: InsertCredential :exec
|
||||
INSERT INTO credential (
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
) VALUES (
|
||||
?1,
|
||||
?2,
|
||||
?3,
|
||||
?4,
|
||||
?5,
|
||||
?6,
|
||||
?7,
|
||||
?8,
|
||||
?9,
|
||||
?10,
|
||||
?11,
|
||||
?12
|
||||
)
|
||||
`
|
||||
|
||||
type InsertCredentialParams struct {
|
||||
CredID string
|
||||
CredPublicKey []byte
|
||||
SignCount int64
|
||||
Transports *string
|
||||
UserVerified *bool
|
||||
BackupEligible *bool
|
||||
BackupState *bool
|
||||
AttestationObject []byte
|
||||
AttestationClientData []byte
|
||||
CreatedAt time.Time
|
||||
LastUsed time.Time
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) InsertCredential(ctx context.Context, arg InsertCredentialParams) error {
|
||||
_, err := q.db.ExecContext(ctx, insertCredential,
|
||||
arg.CredID,
|
||||
arg.CredPublicKey,
|
||||
arg.SignCount,
|
||||
arg.Transports,
|
||||
arg.UserVerified,
|
||||
arg.BackupEligible,
|
||||
arg.BackupState,
|
||||
arg.AttestationObject,
|
||||
arg.AttestationClientData,
|
||||
arg.CreatedAt,
|
||||
arg.LastUsed,
|
||||
arg.UserID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateCredential = `-- name: UpdateCredential :exec
|
||||
UPDATE credential
|
||||
SET
|
||||
last_used = COALESCE(?1, last_used),
|
||||
sign_count = COALESCE(?2, sign_count)
|
||||
WHERE
|
||||
cred_id = ?3
|
||||
AND
|
||||
user_id = ?4
|
||||
`
|
||||
|
||||
type UpdateCredentialParams struct {
|
||||
LastUsed *time.Time
|
||||
SignCount *int64
|
||||
ID string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCredential(ctx context.Context, arg UpdateCredentialParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateCredential,
|
||||
arg.LastUsed,
|
||||
arg.SignCount,
|
||||
arg.ID,
|
||||
arg.UserID,
|
||||
)
|
||||
return err
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
|
||||
package sqlc
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
// source: file.sql
|
||||
|
||||
package sqlc
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
// source: item.sql
|
||||
|
||||
package sqlc
|
||||
@ -85,6 +85,7 @@ WHERE
|
||||
AND
|
||||
(added <= ?4 OR ?4 IS NULL)
|
||||
)
|
||||
ORDER BY added DESC
|
||||
LIMIT
|
||||
?6
|
||||
OFFSET
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
|
||||
package sqlc
|
||||
|
||||
@ -8,6 +8,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
CredID string
|
||||
CredPublicKey []byte
|
||||
SignCount int64
|
||||
Transports *string
|
||||
UserVerified *bool
|
||||
BackupEligible *bool
|
||||
BackupState *bool
|
||||
AttestationObject []byte
|
||||
AttestationClientData []byte
|
||||
CreatedAt time.Time
|
||||
LastUsed time.Time
|
||||
UserID int64
|
||||
}
|
||||
|
||||
type File struct {
|
||||
ID int64
|
||||
Name string
|
||||
@ -34,4 +49,5 @@ type User struct {
|
||||
Username string
|
||||
Password string
|
||||
ProfilePictureID *int64
|
||||
WebauthnID string
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
// source: user.sql
|
||||
|
||||
package sqlc
|
||||
@ -24,7 +24,8 @@ SELECT
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
profile_picture_id
|
||||
profile_picture_id,
|
||||
webauthn_id
|
||||
FROM user
|
||||
WHERE
|
||||
id = ?1
|
||||
@ -39,6 +40,7 @@ func (q *Queries) GetUser(ctx context.Context, id int64) (User, error) {
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.ProfilePictureID,
|
||||
&i.WebauthnID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -48,7 +50,8 @@ SELECT
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
profile_picture_id
|
||||
profile_picture_id,
|
||||
webauthn_id
|
||||
FROM user
|
||||
WHERE
|
||||
username = ?1
|
||||
@ -63,6 +66,7 @@ func (q *Queries) GetUserbyUsername(ctx context.Context, username string) (User,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.ProfilePictureID,
|
||||
&i.WebauthnID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -70,21 +74,24 @@ func (q *Queries) GetUserbyUsername(ctx context.Context, username string) (User,
|
||||
const insertUser = `-- name: InsertUser :one
|
||||
INSERT INTO user (
|
||||
username,
|
||||
password
|
||||
password,
|
||||
webauthn_id
|
||||
) VALUES (
|
||||
?1,
|
||||
?2
|
||||
?2,
|
||||
?3
|
||||
)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
type InsertUserParams struct {
|
||||
Username string
|
||||
Password string
|
||||
Username string
|
||||
Password string
|
||||
WebauthnID string
|
||||
}
|
||||
|
||||
func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (int64, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertUser, arg.Username, arg.Password)
|
||||
row := q.db.QueryRowContext(ctx, insertUser, arg.Username, arg.Password, arg.WebauthnID)
|
||||
var id int64
|
||||
err := row.Scan(&id)
|
||||
return id, err
|
||||
|
Loading…
x
Reference in New Issue
Block a user