Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] 127c8401fe chore(deps): bump node from 26.2-alpine3.23 to 26.3-alpine3.23
Bumps node from 26.2-alpine3.23 to 26.3-alpine3.23.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 26.3-alpine3.23
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-02 01:30:14 +00:00
31 changed files with 1927 additions and 715 deletions
+2 -2
View File
@@ -7,9 +7,9 @@ TINYAUTH_APPURL=
# database config # database config
# The database driver to use. Valid values: sqlite, postgres, memory. # The database driver to use. Valid values: sqlite, memory.
TINYAUTH_DATABASE_DRIVER="sqlite" TINYAUTH_DATABASE_DRIVER="sqlite"
# The path to the SQLite database file, or connection URL when driver is postgres. # The path to the SQLite database, including file name. Only used when driver is sqlite.
TINYAUTH_DATABASE_PATH="./tinyauth.db" TINYAUTH_DATABASE_PATH="./tinyauth.db"
# analytics config # analytics config
+1 -1
View File
@@ -1,5 +1,5 @@
# Site builder # Site builder
FROM node:26.2-alpine3.23 AS frontend-builder FROM node:26.3-alpine3.23 AS frontend-builder
WORKDIR /frontend WORKDIR /frontend
+1 -1
View File
@@ -1,5 +1,5 @@
# Site builder # Site builder
FROM node:26.2-alpine3.23 AS frontend-builder FROM node:26.3-alpine3.23 AS frontend-builder
WORKDIR /frontend WORKDIR /frontend
@@ -1,46 +0,0 @@
DROP TABLE IF EXISTS "oidc_sessions";
CREATE TABLE "oidc_codes" (
"sub" TEXT NOT NULL UNIQUE,
"code_hash" TEXT NOT NULL PRIMARY KEY,
"scope" TEXT NOT NULL,
"redirect_uri" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"expires_at" BIGINT NOT NULL,
"nonce" TEXT NOT NULL DEFAULT '',
"code_challenge" TEXT NOT NULL DEFAULT ''
);
CREATE TABLE "oidc_tokens" (
"sub" TEXT NOT NULL UNIQUE,
"access_token_hash" TEXT NOT NULL PRIMARY KEY,
"refresh_token_hash" TEXT NOT NULL,
"code_hash" TEXT NOT NULL,
"scope" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"token_expires_at" BIGINT NOT NULL,
"refresh_token_expires_at" BIGINT NOT NULL,
"nonce" TEXT NOT NULL DEFAULT ''
);
CREATE TABLE "oidc_userinfo" (
"sub" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"preferred_username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"groups" TEXT NOT NULL,
"updated_at" BIGINT NOT NULL,
"given_name" TEXT NOT NULL,
"family_name" TEXT NOT NULL,
"middle_name" TEXT NOT NULL,
"nickname" TEXT NOT NULL,
"profile" TEXT NOT NULL,
"picture" TEXT NOT NULL,
"website" TEXT NOT NULL,
"gender" TEXT NOT NULL,
"birthdate" TEXT NOT NULL,
"zoneinfo" TEXT NOT NULL,
"locale" TEXT NOT NULL,
"phone_number" TEXT NOT NULL,
"address" TEXT NOT NULL
);
@@ -1,28 +0,0 @@
/*
This migration will nuke the entire setup of OIDC sessions and merge everything
into one table.
*/
/*
Drop all the old tables. Yes, we will log out all OIDC users, but not really a big deal
*/
DROP TABLE IF EXISTS "oidc_tokens";
DROP TABLE IF EXISTS "oidc_userinfo";
DROP TABLE IF EXISTS "oidc_codes";
/*
Create a new simple OIDC sessions table that will hold tokens + userinfo.
*/
CREATE TABLE IF NOT EXISTS "oidc_sessions" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
"access_token_hash" TEXT NOT NULL UNIQUE,
"refresh_token_hash" TEXT NOT NULL UNIQUE,
"scope" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"token_expires_at" BIGINT NOT NULL,
"refresh_token_expires_at" BIGINT NOT NULL,
"nonce" TEXT NOT NULL DEFAULT '',
"userinfo_json" TEXT NOT NULL
);
@@ -1,46 +0,0 @@
DROP TABLE IF EXISTS "oidc_sessions";
CREATE TABLE IF NOT EXISTS "oidc_codes" (
"sub" TEXT NOT NULL UNIQUE,
"code_hash" TEXT NOT NULL PRIMARY KEY UNIQUE,
"scope" TEXT NOT NULL,
"redirect_uri" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"expires_at" INTEGER NOT NULL,
"nonce" TEXT DEFAULT "",
"code_challenge" TEXT DEFAULT ""
);
CREATE TABLE IF NOT EXISTS "oidc_tokens" (
"sub" TEXT NOT NULL UNIQUE,
"access_token_hash" TEXT NOT NULL PRIMARY KEY UNIQUE,
"refresh_token_hash" TEXT NOT NULL,
"code_hash" TEXT NOT NULL,
"scope" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"token_expires_at" INTEGER NOT NULL,
"refresh_token_expires_at" INTEGER NOT NULL,
"nonce" TEXT DEFAULT ""
);
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
"name" TEXT NOT NULL,
"preferred_username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"groups" TEXT NOT NULL,
"updated_at" INTEGER NOT NULL,
"given_name" TEXT NOT NULL,
"family_name" TEXT NOT NULL,
"middle_name" TEXT NOT NULL,
"nickname" TEXT NOT NULL,
"profile" TEXT NOT NULL,
"picture" TEXT NOT NULL,
"website" TEXT NOT NULL,
"gender" TEXT NOT NULL,
"birthdate" TEXT NOT NULL,
"zoneinfo" TEXT NOT NULL,
"locale" TEXT NOT NULL,
"phone_number" TEXT NOT NULL,
"address" TEXT NOT NULL
);
@@ -1,28 +0,0 @@
/*
This migration will nuke the entire setup of OIDC sessions and merge everything
into one table.
*/
/*
Drop all the old tables. Yes, we will log out all OIDC users, but not really a big deal
*/
DROP TABLE IF EXISTS "oidc_tokens";
DROP TABLE IF EXISTS "oidc_userinfo";
DROP TABLE IF EXISTS "oidc_codes";
/*
Create a new simple OIDC sessions table that will hold tokens + userinfo.
*/
CREATE TABLE IF NOT EXISTS "oidc_sessions" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
"access_token_hash" TEXT NOT NULL UNIQUE,
"refresh_token_hash" TEXT NOT NULL UNIQUE,
"scope" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"token_expires_at" INTEGER NOT NULL,
"refresh_token_expires_at" INTEGER NOT NULL,
"nonce" TEXT DEFAULT "",
"userinfo_json" TEXT NOT NULL
);
+3 -2
View File
@@ -15,14 +15,15 @@ import (
"github.com/tinyauthapp/tinyauth/internal/assets" "github.com/tinyauthapp/tinyauth/internal/assets"
"github.com/tinyauthapp/tinyauth/internal/repository" "github.com/tinyauthapp/tinyauth/internal/repository"
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
"github.com/tinyauthapp/tinyauth/internal/repository/postgres" "github.com/tinyauthapp/tinyauth/internal/repository/postgres"
"github.com/tinyauthapp/tinyauth/internal/repository/sqlite" "github.com/tinyauthapp/tinyauth/internal/repository/sqlite"
) )
func (app *BootstrapApp) SetupStore() (repository.Store, error) { func (app *BootstrapApp) SetupStore() (repository.Store, error) {
switch app.config.Database.Driver { switch app.config.Database.Driver {
// case "memory": case "memory":
// return memory.New(), nil return memory.New(), nil
case "sqlite", "": case "sqlite", "":
return app.setupSQLite(app.config.Database.Path) return app.setupSQLite(app.config.Database.Path)
case "postgres": case "postgres":
+73 -22
View File
@@ -1,7 +1,6 @@
package controller package controller
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@@ -13,6 +12,7 @@ import (
"github.com/tinyauthapp/tinyauth/internal/model" "github.com/tinyauthapp/tinyauth/internal/model"
"github.com/tinyauthapp/tinyauth/internal/service" "github.com/tinyauthapp/tinyauth/internal/service"
"github.com/tinyauthapp/tinyauth/internal/utils"
"github.com/tinyauthapp/tinyauth/internal/utils/logger" "github.com/tinyauthapp/tinyauth/internal/utils/logger"
) )
@@ -169,7 +169,7 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
return return
} }
_, ok := controller.oidc.GetClient(req.ClientID) client, ok := controller.oidc.GetClient(req.ClientID)
if !ok { if !ok {
controller.authorizeError(c, authorizeErrorParams{ controller.authorizeError(c, authorizeErrorParams{
@@ -203,8 +203,9 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
return return
} }
// Create the sub to find and delete old sessions // WARNING: Since Tinyauth is stateless, we cannot have a sub that never changes. We will just create a uuid out of the username and client name which remains stable, but if username or client name changes then sub changes too.
sub := controller.oidc.CreateSub(*userContext, req.ClientID) sub := utils.GenerateUUID(fmt.Sprintf("%s:%s", userContext.GetUsername(), client.ID))
code := utils.GenerateString(32)
// Before storing the code, delete old session // Before storing the code, delete old session
err = controller.oidc.DeleteOldSession(c, sub) err = controller.oidc.DeleteOldSession(c, sub)
@@ -220,8 +221,37 @@ func (controller *OIDCController) Authorize(c *gin.Context) {
return return
} }
// Create the authorization code err = controller.oidc.StoreCode(c, sub, code, req)
code := controller.oidc.CreateCode(req, *userContext)
if err != nil {
controller.authorizeError(c, authorizeErrorParams{
err: err,
reason: "Failed to store code",
reasonPublic: "Failed to store code",
callback: req.RedirectURI,
callbackError: "server_error",
state: req.State,
})
return
}
// We also need a snapshot of the user that authorized this (skip if no openid scope)
if slices.Contains(strings.Fields(req.Scope), "openid") {
err = controller.oidc.StoreUserinfo(c, sub, *userContext, req)
if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to store user info")
controller.authorizeError(c, authorizeErrorParams{
err: err,
reason: "Failed to store user info",
reasonPublic: "Failed to store user info",
callback: req.RedirectURI,
callbackError: "server_error",
state: req.State,
})
return
}
}
queries, err := query.Values(AuthorizeCallback{ queries, err := query.Values(AuthorizeCallback{
Code: code, Code: code,
@@ -324,12 +354,35 @@ func (controller *OIDCController) Token(c *gin.Context) {
switch req.GrantType { switch req.GrantType {
case "authorization_code": case "authorization_code":
entry, ok := controller.oidc.GetCodeEntry(controller.oidc.Hash(req.Code), client.ClientID) entry, err := controller.oidc.GetCodeEntry(c, controller.oidc.Hash(req.Code), client.ClientID)
if err != nil {
if !ok { if err := controller.oidc.DeleteTokenByCodeHash(c, controller.oidc.Hash(req.Code)); err != nil {
controller.log.App.Warn().Msg("Code not found") controller.log.App.Error().Err(err).Msg("Failed to revoke tokens for replayed code")
}
if errors.Is(err, service.ErrCodeNotFound) {
controller.log.App.Warn().Msg("Code not found")
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
if errors.Is(err, service.ErrCodeExpired) {
controller.log.App.Warn().Msg("Code expired")
c.JSON(400, gin.H{
"error": "invalid_grant",
})
return
}
if errors.Is(err, service.ErrInvalidClient) {
controller.log.App.Warn().Msg("Code does not belong to client")
c.JSON(400, gin.H{
"error": "invalid_client",
})
return
}
controller.log.App.Error().Err(err).Msg("Failed to get code entry")
c.JSON(400, gin.H{ c.JSON(400, gin.H{
"error": "invalid_grant", "error": "server_error",
}) })
return return
} }
@@ -342,7 +395,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
return return
} }
ok = controller.oidc.ValidatePKCE(entry.CodeChallenge, req.CodeVerifier) ok := controller.oidc.ValidatePKCE(entry.CodeChallenge, req.CodeVerifier)
if !ok { if !ok {
controller.log.App.Warn().Msg("PKCE validation failed") controller.log.App.Warn().Msg("PKCE validation failed")
@@ -352,7 +405,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
return return
} }
tokenRes, err := controller.oidc.GenerateAccessToken(c, client, *entry) tokenRes, err := controller.oidc.GenerateAccessToken(c, client, entry)
if err != nil { if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to generate access token") controller.log.App.Error().Err(err).Msg("Failed to generate access token")
@@ -362,7 +415,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
return return
} }
tokenResponse = *tokenRes tokenResponse = tokenRes
case "refresh_token": case "refresh_token":
tokenRes, err := controller.oidc.RefreshAccessToken(c, req.RefreshToken, creds.ClientID) tokenRes, err := controller.oidc.RefreshAccessToken(c, req.RefreshToken, creds.ClientID)
@@ -390,7 +443,7 @@ func (controller *OIDCController) Token(c *gin.Context) {
return return
} }
tokenResponse = *tokenRes tokenResponse = tokenRes
} }
c.Header("cache-control", "no-store") c.Header("cache-control", "no-store")
@@ -454,7 +507,7 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
return return
} }
entry, err := controller.oidc.GetSessionByToken(c, controller.oidc.Hash(token)) entry, err := controller.oidc.GetAccessToken(c, controller.oidc.Hash(token))
if err != nil { if err != nil {
if errors.Is(err, service.ErrTokenNotFound) { if errors.Is(err, service.ErrTokenNotFound) {
@@ -473,17 +526,15 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
} }
// If we don't have the openid scope, return an error // If we don't have the openid scope, return an error
if !slices.Contains(strings.Split(entry.Scope, " "), "openid") { if !slices.Contains(strings.Split(entry.Scope, ","), "openid") {
controller.log.App.Warn().Msg("OIDC userinfo accessed with missing openid scope") controller.log.App.Warn().Msg("OIDC userinfo accessed with token missing openid scope")
c.JSON(401, gin.H{ c.JSON(401, gin.H{
"error": "invalid_scope", "error": "invalid_scope",
}) })
return return
} }
var userinfo service.UserinfoResponse user, err := controller.oidc.GetUserinfo(c, entry.Sub)
err = json.Unmarshal([]byte(entry.UserinfoJson), &userinfo)
if err != nil { if err != nil {
controller.log.App.Error().Err(err).Msg("Failed to get user info") controller.log.App.Error().Err(err).Msg("Failed to get user info")
@@ -493,7 +544,7 @@ func (controller *OIDCController) Userinfo(c *gin.Context) {
return return
} }
c.JSON(200, controller.oidc.CompileUserinfo(userinfo, entry.Scope)) c.JSON(200, controller.oidc.CompileUserinfo(user, entry.Scope))
} }
func (controller *OIDCController) authorizeError(c *gin.Context, params authorizeErrorParams) { func (controller *OIDCController) authorizeError(c *gin.Context, params authorizeErrorParams) {
@@ -1,7 +1,3 @@
//go:build exclude
// temporary
package memory_test package memory_test
import ( import (
@@ -1,7 +1,3 @@
//go:build exclude
// temporary
package memory package memory
import ( import (
@@ -1,7 +1,3 @@
//go:build exclude
// temporary
package memory package memory
import ( import (
+9 -9
View File
@@ -1,7 +1,3 @@
//go:build exclude
// temporary
// Package memory provides an in-memory implementation of repository.Store for use in tests. // Package memory provides an in-memory implementation of repository.Store for use in tests.
package memory package memory
@@ -13,15 +9,19 @@ import (
// Store is a thread-safe in-memory implementation of repository.Store. // Store is a thread-safe in-memory implementation of repository.Store.
type Store struct { type Store struct {
mu sync.RWMutex mu sync.RWMutex
sessions map[string]repository.Session sessions map[string]repository.Session
oidcSessions map[string]repository.OidcSession oidcCodes map[string]repository.OidcCode
oidcTokens map[string]repository.OidcToken
oidcUsers map[string]repository.OidcUserinfo
} }
// New returns a new empty in-memory Store. // New returns a new empty in-memory Store.
func New() repository.Store { func New() repository.Store {
return &Store{ return &Store{
sessions: make(map[string]repository.Session), sessions: make(map[string]repository.Session),
oidcSessions: make(map[string]repository.OidcSession), oidcCodes: make(map[string]repository.OidcCode),
oidcTokens: make(map[string]repository.OidcToken),
oidcUsers: make(map[string]repository.OidcUserinfo),
} }
} }
+73 -11
View File
@@ -17,16 +17,49 @@ type Session struct {
OAuthSub string OAuthSub string
} }
type OidcSession struct { type OidcCode struct {
Sub string
CodeHash string
Scope string
RedirectURI string
ClientID string
ExpiresAt int64
Nonce string
CodeChallenge string
}
type OidcToken struct {
Sub string Sub string
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
CodeHash string
Scope string Scope string
ClientID string ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
Nonce string Nonce string
UserinfoJson string }
type OidcUserinfo struct {
Sub string
Name string
PreferredUsername string
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
} }
type CreateSessionParams struct { type CreateSessionParams struct {
@@ -56,7 +89,18 @@ type UpdateSessionParams struct {
UUID string UUID string
} }
type CreateOIDCSessionParams struct { type CreateOidcCodeParams struct {
Sub string
CodeHash string
Scope string
RedirectURI string
ClientID string
ExpiresAt int64
Nonce string
CodeChallenge string
}
type CreateOidcTokenParams struct {
Sub string Sub string
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
@@ -64,23 +108,41 @@ type CreateOIDCSessionParams struct {
ClientID string ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
CodeHash string
Nonce string Nonce string
UserinfoJson string
} }
type UpdateOIDCSessionParams struct { type UpdateOidcTokenByRefreshTokenParams struct {
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
Scope string
ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
Nonce string RefreshTokenHash_2 string
UserinfoJson string
Sub string
} }
type DeleteExpiredOIDCSessionsParams struct { type DeleteExpiredOidcTokensParams struct {
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
} }
type CreateOidcUserInfoParams struct {
Sub string
Name string
PreferredUsername string
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
}
+1 -1
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
package postgres package postgres
+36 -3
View File
@@ -1,19 +1,52 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
package postgres package postgres
type OidcSession struct { type OidcCode struct {
Sub string
CodeHash string
Scope string
RedirectURI string
ClientID string
ExpiresAt int64
Nonce string
CodeChallenge string
}
type OidcToken struct {
Sub string Sub string
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
CodeHash string
Scope string Scope string
ClientID string ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
Nonce string Nonce string
UserinfoJson string }
type OidcUserinfo struct {
Sub string
Name string
PreferredUsername string
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
} }
type Session struct { type Session struct {
+481 -110
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
// source: oidc_queries.sql // source: oidc_queries.sql
package postgres package postgres
@@ -9,8 +9,60 @@ import (
"context" "context"
) )
const createOIDCSession = `-- name: CreateOIDCSession :one const createOidcCode = `-- name: CreateOidcCode :one
INSERT INTO "oidc_sessions" ( INSERT INTO "oidc_codes" (
"sub",
"code_hash",
"scope",
"redirect_uri",
"client_id",
"expires_at",
"nonce",
"code_challenge"
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8
)
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
type CreateOidcCodeParams struct {
Sub string
CodeHash string
Scope string
RedirectURI string
ClientID string
ExpiresAt int64
Nonce string
CodeChallenge string
}
func (q *Queries) CreateOidcCode(ctx context.Context, arg CreateOidcCodeParams) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, createOidcCode,
arg.Sub,
arg.CodeHash,
arg.Scope,
arg.RedirectURI,
arg.ClientID,
arg.ExpiresAt,
arg.Nonce,
arg.CodeChallenge,
)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const createOidcToken = `-- name: CreateOidcToken :one
INSERT INTO "oidc_tokens" (
"sub", "sub",
"access_token_hash", "access_token_hash",
"refresh_token_hash", "refresh_token_hash",
@@ -18,15 +70,15 @@ INSERT INTO "oidc_sessions" (
"client_id", "client_id",
"token_expires_at", "token_expires_at",
"refresh_token_expires_at", "refresh_token_expires_at",
"nonce", "code_hash",
"userinfo_json" "nonce"
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9 $1, $2, $3, $4, $5, $6, $7, $8, $9
) )
RETURNING sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
` `
type CreateOIDCSessionParams struct { type CreateOidcTokenParams struct {
Sub string Sub string
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
@@ -34,12 +86,12 @@ type CreateOIDCSessionParams struct {
ClientID string ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
CodeHash string
Nonce string Nonce string
UserinfoJson string
} }
func (q *Queries) CreateOIDCSession(ctx context.Context, arg CreateOIDCSessionParams) (OidcSession, error) { func (q *Queries) CreateOidcToken(ctx context.Context, arg CreateOidcTokenParams) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, createOIDCSession, row := q.db.QueryRowContext(ctx, createOidcToken,
arg.Sub, arg.Sub,
arg.AccessTokenHash, arg.AccessTokenHash,
arg.RefreshTokenHash, arg.RefreshTokenHash,
@@ -47,164 +99,483 @@ func (q *Queries) CreateOIDCSession(ctx context.Context, arg CreateOIDCSessionPa
arg.ClientID, arg.ClientID,
arg.TokenExpiresAt, arg.TokenExpiresAt,
arg.RefreshTokenExpiresAt, arg.RefreshTokenExpiresAt,
arg.CodeHash,
arg.Nonce, arg.Nonce,
arg.UserinfoJson,
) )
var i OidcSession var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
const deleteExpiredOIDCSessions = `-- name: DeleteExpiredOIDCSessions :exec const createOidcUserInfo = `-- name: CreateOidcUserInfo :one
DELETE FROM "oidc_sessions" INSERT INTO "oidc_userinfo" (
WHERE "token_expires_at" < $1 AND "refresh_token_expires_at" < $2 "sub",
"name",
"preferred_username",
"email",
"groups",
"updated_at",
"given_name",
"family_name",
"middle_name",
"nickname",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"phone_number",
"address"
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19
)
RETURNING sub, name, preferred_username, email, groups, updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address
` `
type DeleteExpiredOIDCSessionsParams struct { type CreateOidcUserInfoParams struct {
Sub string
Name string
PreferredUsername string
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
}
func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error) {
row := q.db.QueryRowContext(ctx, createOidcUserInfo,
arg.Sub,
arg.Name,
arg.PreferredUsername,
arg.Email,
arg.Groups,
arg.UpdatedAt,
arg.GivenName,
arg.FamilyName,
arg.MiddleName,
arg.Nickname,
arg.Profile,
arg.Picture,
arg.Website,
arg.Gender,
arg.Birthdate,
arg.Zoneinfo,
arg.Locale,
arg.PhoneNumber,
arg.Address,
)
var i OidcUserinfo
err := row.Scan(
&i.Sub,
&i.Name,
&i.PreferredUsername,
&i.Email,
&i.Groups,
&i.UpdatedAt,
&i.GivenName,
&i.FamilyName,
&i.MiddleName,
&i.Nickname,
&i.Profile,
&i.Picture,
&i.Website,
&i.Gender,
&i.Birthdate,
&i.Zoneinfo,
&i.Locale,
&i.PhoneNumber,
&i.Address,
)
return i, err
}
const deleteExpiredOidcCodes = `-- name: DeleteExpiredOidcCodes :many
DELETE FROM "oidc_codes"
WHERE "expires_at" < $1
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
func (q *Queries) DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]OidcCode, error) {
rows, err := q.db.QueryContext(ctx, deleteExpiredOidcCodes, expiresAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OidcCode
for rows.Next() {
var i OidcCode
if err := rows.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
); 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 deleteExpiredOidcTokens = `-- name: DeleteExpiredOidcTokens :many
DELETE FROM "oidc_tokens"
WHERE "token_expires_at" < $1 AND "refresh_token_expires_at" < $2
RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
`
type DeleteExpiredOidcTokensParams struct {
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
} }
func (q *Queries) DeleteExpiredOIDCSessions(ctx context.Context, arg DeleteExpiredOIDCSessionsParams) error { func (q *Queries) DeleteExpiredOidcTokens(ctx context.Context, arg DeleteExpiredOidcTokensParams) ([]OidcToken, error) {
_, err := q.db.ExecContext(ctx, deleteExpiredOIDCSessions, arg.TokenExpiresAt, arg.RefreshTokenExpiresAt) rows, err := q.db.QueryContext(ctx, deleteExpiredOidcTokens, arg.TokenExpiresAt, arg.RefreshTokenExpiresAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OidcToken
for rows.Next() {
var i OidcToken
if err := rows.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.CodeHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
); 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 deleteOidcCode = `-- name: DeleteOidcCode :exec
DELETE FROM "oidc_codes"
WHERE "code_hash" = $1
`
func (q *Queries) DeleteOidcCode(ctx context.Context, codeHash string) error {
_, err := q.db.ExecContext(ctx, deleteOidcCode, codeHash)
return err return err
} }
const deleteOIDCSessionBySub = `-- name: DeleteOIDCSessionBySub :exec const deleteOidcCodeBySub = `-- name: DeleteOidcCodeBySub :exec
DELETE FROM "oidc_sessions" DELETE FROM "oidc_codes"
WHERE "sub" = $1 WHERE "sub" = $1
` `
func (q *Queries) DeleteOIDCSessionBySub(ctx context.Context, sub string) error { func (q *Queries) DeleteOidcCodeBySub(ctx context.Context, sub string) error {
_, err := q.db.ExecContext(ctx, deleteOIDCSessionBySub, sub) _, err := q.db.ExecContext(ctx, deleteOidcCodeBySub, sub)
return err return err
} }
const getOIDCSessionByAccessTokenHash = `-- name: GetOIDCSessionByAccessTokenHash :one const deleteOidcToken = `-- name: DeleteOidcToken :exec
SELECT sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json FROM "oidc_sessions" DELETE FROM "oidc_tokens"
WHERE "access_token_hash" = $1 WHERE "access_token_hash" = $1
` `
func (q *Queries) GetOIDCSessionByAccessTokenHash(ctx context.Context, accessTokenHash string) (OidcSession, error) { func (q *Queries) DeleteOidcToken(ctx context.Context, accessTokenHash string) error {
row := q.db.QueryRowContext(ctx, getOIDCSessionByAccessTokenHash, accessTokenHash) _, err := q.db.ExecContext(ctx, deleteOidcToken, accessTokenHash)
var i OidcSession return err
err := row.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
&i.UserinfoJson,
)
return i, err
} }
const getOIDCSessionByRefreshTokenHash = `-- name: GetOIDCSessionByRefreshTokenHash :one const deleteOidcTokenByCodeHash = `-- name: DeleteOidcTokenByCodeHash :exec
SELECT sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json FROM "oidc_sessions" DELETE FROM "oidc_tokens"
WHERE "refresh_token_hash" = $1 WHERE "code_hash" = $1
` `
func (q *Queries) GetOIDCSessionByRefreshTokenHash(ctx context.Context, refreshTokenHash string) (OidcSession, error) { func (q *Queries) DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error {
row := q.db.QueryRowContext(ctx, getOIDCSessionByRefreshTokenHash, refreshTokenHash) _, err := q.db.ExecContext(ctx, deleteOidcTokenByCodeHash, codeHash)
var i OidcSession return err
err := row.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
&i.UserinfoJson,
)
return i, err
} }
const getOIDCSessionBySub = `-- name: GetOIDCSessionBySub :one const deleteOidcTokenBySub = `-- name: DeleteOidcTokenBySub :exec
SELECT sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json FROM "oidc_sessions" DELETE FROM "oidc_tokens"
WHERE "sub" = $1 WHERE "sub" = $1
` `
func (q *Queries) GetOIDCSessionBySub(ctx context.Context, sub string) (OidcSession, error) { func (q *Queries) DeleteOidcTokenBySub(ctx context.Context, sub string) error {
row := q.db.QueryRowContext(ctx, getOIDCSessionBySub, sub) _, err := q.db.ExecContext(ctx, deleteOidcTokenBySub, sub)
var i OidcSession return err
err := row.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
&i.UserinfoJson,
)
return i, err
} }
const updateOIDCSession = `-- name: UpdateOIDCSession :one const deleteOidcUserInfo = `-- name: DeleteOidcUserInfo :exec
UPDATE "oidc_sessions" SET DELETE FROM "oidc_userinfo"
"access_token_hash" = $1, WHERE "sub" = $1
"refresh_token_hash" = $2,
"scope" = $3,
"client_id" = $4,
"token_expires_at" = $5,
"refresh_token_expires_at" = $6,
"nonce" = $7,
"userinfo_json" = $8
WHERE "sub" = $9
RETURNING sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json
` `
type UpdateOIDCSessionParams struct { func (q *Queries) DeleteOidcUserInfo(ctx context.Context, sub string) error {
AccessTokenHash string _, err := q.db.ExecContext(ctx, deleteOidcUserInfo, sub)
RefreshTokenHash string return err
Scope string
ClientID string
TokenExpiresAt int64
RefreshTokenExpiresAt int64
Nonce string
UserinfoJson string
Sub string
} }
func (q *Queries) UpdateOIDCSession(ctx context.Context, arg UpdateOIDCSessionParams) (OidcSession, error) { const getOidcCode = `-- name: GetOidcCode :one
row := q.db.QueryRowContext(ctx, updateOIDCSession, DELETE FROM "oidc_codes"
arg.AccessTokenHash, WHERE "code_hash" = $1
arg.RefreshTokenHash, RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
arg.Scope, `
arg.ClientID,
arg.TokenExpiresAt, func (q *Queries) GetOidcCode(ctx context.Context, codeHash string) (OidcCode, error) {
arg.RefreshTokenExpiresAt, row := q.db.QueryRowContext(ctx, getOidcCode, codeHash)
arg.Nonce, var i OidcCode
arg.UserinfoJson, err := row.Scan(
arg.Sub, &i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
) )
var i OidcSession return i, err
}
const getOidcCodeBySub = `-- name: GetOidcCodeBySub :one
DELETE FROM "oidc_codes"
WHERE "sub" = $1
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
func (q *Queries) GetOidcCodeBySub(ctx context.Context, sub string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCodeBySub, sub)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcCodeBySubUnsafe = `-- name: GetOidcCodeBySubUnsafe :one
SELECT sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge FROM "oidc_codes"
WHERE "sub" = $1
`
func (q *Queries) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCodeBySubUnsafe, sub)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcCodeUnsafe = `-- name: GetOidcCodeUnsafe :one
SELECT sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge FROM "oidc_codes"
WHERE "code_hash" = $1
`
func (q *Queries) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCodeUnsafe, codeHash)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcToken = `-- name: GetOidcToken :one
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
WHERE "access_token_hash" = $1
`
func (q *Queries) GetOidcToken(ctx context.Context, accessTokenHash string) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, getOidcToken, accessTokenHash)
var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
)
return i, err
}
const getOidcTokenByRefreshToken = `-- name: GetOidcTokenByRefreshToken :one
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
WHERE "refresh_token_hash" = $1
`
func (q *Queries) GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, getOidcTokenByRefreshToken, refreshTokenHash)
var i OidcToken
err := row.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.CodeHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
)
return i, err
}
const getOidcTokenBySub = `-- name: GetOidcTokenBySub :one
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
WHERE "sub" = $1
`
func (q *Queries) GetOidcTokenBySub(ctx context.Context, sub string) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, getOidcTokenBySub, sub)
var i OidcToken
err := row.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.CodeHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
)
return i, err
}
const getOidcUserInfo = `-- name: GetOidcUserInfo :one
SELECT sub, name, preferred_username, email, groups, updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address FROM "oidc_userinfo"
WHERE "sub" = $1
`
func (q *Queries) GetOidcUserInfo(ctx context.Context, sub string) (OidcUserinfo, error) {
row := q.db.QueryRowContext(ctx, getOidcUserInfo, sub)
var i OidcUserinfo
err := row.Scan(
&i.Sub,
&i.Name,
&i.PreferredUsername,
&i.Email,
&i.Groups,
&i.UpdatedAt,
&i.GivenName,
&i.FamilyName,
&i.MiddleName,
&i.Nickname,
&i.Profile,
&i.Picture,
&i.Website,
&i.Gender,
&i.Birthdate,
&i.Zoneinfo,
&i.Locale,
&i.PhoneNumber,
&i.Address,
)
return i, err
}
const updateOidcTokenByRefreshToken = `-- name: UpdateOidcTokenByRefreshToken :one
UPDATE "oidc_tokens" SET
"access_token_hash" = $1,
"refresh_token_hash" = $2,
"token_expires_at" = $3,
"refresh_token_expires_at" = $4
WHERE "refresh_token_hash" = $5
RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
`
type UpdateOidcTokenByRefreshTokenParams struct {
AccessTokenHash string
RefreshTokenHash string
TokenExpiresAt int64
RefreshTokenExpiresAt int64
RefreshTokenHash_2 string
}
func (q *Queries) UpdateOidcTokenByRefreshToken(ctx context.Context, arg UpdateOidcTokenByRefreshTokenParams) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, updateOidcTokenByRefreshToken,
arg.AccessTokenHash,
arg.RefreshTokenHash,
arg.TokenExpiresAt,
arg.RefreshTokenExpiresAt,
arg.RefreshTokenHash_2,
)
var i OidcToken
err := row.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
// source: session_queries.sql // source: session_queries.sql
package postgres package postgres
+120 -24
View File
@@ -32,12 +32,28 @@ func mapErr(err error) error {
return err return err
} }
func (s *Store) CreateOIDCSession(ctx context.Context, arg repository.CreateOIDCSessionParams) (repository.OidcSession, error) { func (s *Store) CreateOidcCode(ctx context.Context, arg repository.CreateOidcCodeParams) (repository.OidcCode, error) {
r, err := s.q.CreateOIDCSession(ctx, CreateOIDCSessionParams(arg)) r, err := s.q.CreateOidcCode(ctx, CreateOidcCodeParams(arg))
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
}
func (s *Store) CreateOidcToken(ctx context.Context, arg repository.CreateOidcTokenParams) (repository.OidcToken, error) {
r, err := s.q.CreateOidcToken(ctx, CreateOidcTokenParams(arg))
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) CreateOidcUserInfo(ctx context.Context, arg repository.CreateOidcUserInfoParams) (repository.OidcUserinfo, error) {
r, err := s.q.CreateOidcUserInfo(ctx, CreateOidcUserInfoParams(arg))
if err != nil {
return repository.OidcUserinfo{}, mapErr(err)
}
return repository.OidcUserinfo(r), nil
} }
func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionParams) (repository.Session, error) { func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionParams) (repository.Session, error) {
@@ -48,44 +64,124 @@ func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionP
return repository.Session(r), nil return repository.Session(r), nil
} }
func (s *Store) DeleteExpiredOIDCSessions(ctx context.Context, arg repository.DeleteExpiredOIDCSessionsParams) error { func (s *Store) DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]repository.OidcCode, error) {
return mapErr(s.q.DeleteExpiredOIDCSessions(ctx, DeleteExpiredOIDCSessionsParams(arg))) rows, err := s.q.DeleteExpiredOidcCodes(ctx, expiresAt)
if err != nil {
return nil, mapErr(err)
}
out := make([]repository.OidcCode, len(rows))
for i, row := range rows {
out[i] = repository.OidcCode(row)
}
return out, nil
}
func (s *Store) DeleteExpiredOidcTokens(ctx context.Context, arg repository.DeleteExpiredOidcTokensParams) ([]repository.OidcToken, error) {
rows, err := s.q.DeleteExpiredOidcTokens(ctx, DeleteExpiredOidcTokensParams(arg))
if err != nil {
return nil, mapErr(err)
}
out := make([]repository.OidcToken, len(rows))
for i, row := range rows {
out[i] = repository.OidcToken(row)
}
return out, nil
} }
func (s *Store) DeleteExpiredSessions(ctx context.Context, expiry int64) error { func (s *Store) DeleteExpiredSessions(ctx context.Context, expiry int64) error {
return mapErr(s.q.DeleteExpiredSessions(ctx, expiry)) return mapErr(s.q.DeleteExpiredSessions(ctx, expiry))
} }
func (s *Store) DeleteOIDCSessionBySub(ctx context.Context, sub string) error { func (s *Store) DeleteOidcCode(ctx context.Context, codeHash string) error {
return mapErr(s.q.DeleteOIDCSessionBySub(ctx, sub)) return mapErr(s.q.DeleteOidcCode(ctx, codeHash))
}
func (s *Store) DeleteOidcCodeBySub(ctx context.Context, sub string) error {
return mapErr(s.q.DeleteOidcCodeBySub(ctx, sub))
}
func (s *Store) DeleteOidcToken(ctx context.Context, accessTokenHash string) error {
return mapErr(s.q.DeleteOidcToken(ctx, accessTokenHash))
}
func (s *Store) DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error {
return mapErr(s.q.DeleteOidcTokenByCodeHash(ctx, codeHash))
}
func (s *Store) DeleteOidcTokenBySub(ctx context.Context, sub string) error {
return mapErr(s.q.DeleteOidcTokenBySub(ctx, sub))
}
func (s *Store) DeleteOidcUserInfo(ctx context.Context, sub string) error {
return mapErr(s.q.DeleteOidcUserInfo(ctx, sub))
} }
func (s *Store) DeleteSession(ctx context.Context, uuid string) error { func (s *Store) DeleteSession(ctx context.Context, uuid string) error {
return mapErr(s.q.DeleteSession(ctx, uuid)) return mapErr(s.q.DeleteSession(ctx, uuid))
} }
func (s *Store) GetOIDCSessionByAccessTokenHash(ctx context.Context, accessTokenHash string) (repository.OidcSession, error) { func (s *Store) GetOidcCode(ctx context.Context, codeHash string) (repository.OidcCode, error) {
r, err := s.q.GetOIDCSessionByAccessTokenHash(ctx, accessTokenHash) r, err := s.q.GetOidcCode(ctx, codeHash)
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
} }
func (s *Store) GetOIDCSessionByRefreshTokenHash(ctx context.Context, refreshTokenHash string) (repository.OidcSession, error) { func (s *Store) GetOidcCodeBySub(ctx context.Context, sub string) (repository.OidcCode, error) {
r, err := s.q.GetOIDCSessionByRefreshTokenHash(ctx, refreshTokenHash) r, err := s.q.GetOidcCodeBySub(ctx, sub)
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
} }
func (s *Store) GetOIDCSessionBySub(ctx context.Context, sub string) (repository.OidcSession, error) { func (s *Store) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (repository.OidcCode, error) {
r, err := s.q.GetOIDCSessionBySub(ctx, sub) r, err := s.q.GetOidcCodeBySubUnsafe(ctx, sub)
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
}
func (s *Store) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (repository.OidcCode, error) {
r, err := s.q.GetOidcCodeUnsafe(ctx, codeHash)
if err != nil {
return repository.OidcCode{}, mapErr(err)
}
return repository.OidcCode(r), nil
}
func (s *Store) GetOidcToken(ctx context.Context, accessTokenHash string) (repository.OidcToken, error) {
r, err := s.q.GetOidcToken(ctx, accessTokenHash)
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (repository.OidcToken, error) {
r, err := s.q.GetOidcTokenByRefreshToken(ctx, refreshTokenHash)
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) GetOidcTokenBySub(ctx context.Context, sub string) (repository.OidcToken, error) {
r, err := s.q.GetOidcTokenBySub(ctx, sub)
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) GetOidcUserInfo(ctx context.Context, sub string) (repository.OidcUserinfo, error) {
r, err := s.q.GetOidcUserInfo(ctx, sub)
if err != nil {
return repository.OidcUserinfo{}, mapErr(err)
}
return repository.OidcUserinfo(r), nil
} }
func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session, error) { func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session, error) {
@@ -96,12 +192,12 @@ func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session
return repository.Session(r), nil return repository.Session(r), nil
} }
func (s *Store) UpdateOIDCSession(ctx context.Context, arg repository.UpdateOIDCSessionParams) (repository.OidcSession, error) { func (s *Store) UpdateOidcTokenByRefreshToken(ctx context.Context, arg repository.UpdateOidcTokenByRefreshTokenParams) (repository.OidcToken, error) {
r, err := s.q.UpdateOIDCSession(ctx, UpdateOIDCSessionParams(arg)) r, err := s.q.UpdateOidcTokenByRefreshToken(ctx, UpdateOidcTokenByRefreshTokenParams(arg))
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcToken{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcToken(r), nil
} }
func (s *Store) UpdateSession(ctx context.Context, arg repository.UpdateSessionParams) (repository.Session, error) { func (s *Store) UpdateSession(ctx context.Context, arg repository.UpdateSessionParams) (repository.Session, error) {
+1 -1
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
package sqlite package sqlite
+36 -3
View File
@@ -1,19 +1,52 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
package sqlite package sqlite
type OidcSession struct { type OidcCode struct {
Sub string
CodeHash string
Scope string
RedirectURI string
ClientID string
ExpiresAt int64
Nonce string
CodeChallenge string
}
type OidcToken struct {
Sub string Sub string
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
CodeHash string
Scope string Scope string
ClientID string ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
Nonce string Nonce string
UserinfoJson string }
type OidcUserinfo struct {
Sub string
Name string
PreferredUsername string
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
} }
type Session struct { type Session struct {
+436 -65
View File
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
// source: oidc_queries.sql // source: oidc_queries.sql
package sqlite package sqlite
@@ -9,8 +9,60 @@ import (
"context" "context"
) )
const createOIDCSession = `-- name: CreateOIDCSession :one const createOidcCode = `-- name: CreateOidcCode :one
INSERT INTO "oidc_sessions" ( INSERT INTO "oidc_codes" (
"sub",
"code_hash",
"scope",
"redirect_uri",
"client_id",
"expires_at",
"nonce",
"code_challenge"
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
type CreateOidcCodeParams struct {
Sub string
CodeHash string
Scope string
RedirectURI string
ClientID string
ExpiresAt int64
Nonce string
CodeChallenge string
}
func (q *Queries) CreateOidcCode(ctx context.Context, arg CreateOidcCodeParams) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, createOidcCode,
arg.Sub,
arg.CodeHash,
arg.Scope,
arg.RedirectURI,
arg.ClientID,
arg.ExpiresAt,
arg.Nonce,
arg.CodeChallenge,
)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const createOidcToken = `-- name: CreateOidcToken :one
INSERT INTO "oidc_tokens" (
"sub", "sub",
"access_token_hash", "access_token_hash",
"refresh_token_hash", "refresh_token_hash",
@@ -18,15 +70,15 @@ INSERT INTO "oidc_sessions" (
"client_id", "client_id",
"token_expires_at", "token_expires_at",
"refresh_token_expires_at", "refresh_token_expires_at",
"nonce", "code_hash",
"userinfo_json" "nonce"
) VALUES ( ) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ? ?, ?, ?, ?, ?, ?, ?, ?, ?
) )
RETURNING sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
` `
type CreateOIDCSessionParams struct { type CreateOidcTokenParams struct {
Sub string Sub string
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
@@ -34,12 +86,12 @@ type CreateOIDCSessionParams struct {
ClientID string ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
CodeHash string
Nonce string Nonce string
UserinfoJson string
} }
func (q *Queries) CreateOIDCSession(ctx context.Context, arg CreateOIDCSessionParams) (OidcSession, error) { func (q *Queries) CreateOidcToken(ctx context.Context, arg CreateOidcTokenParams) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, createOIDCSession, row := q.db.QueryRowContext(ctx, createOidcToken,
arg.Sub, arg.Sub,
arg.AccessTokenHash, arg.AccessTokenHash,
arg.RefreshTokenHash, arg.RefreshTokenHash,
@@ -47,164 +99,483 @@ func (q *Queries) CreateOIDCSession(ctx context.Context, arg CreateOIDCSessionPa
arg.ClientID, arg.ClientID,
arg.TokenExpiresAt, arg.TokenExpiresAt,
arg.RefreshTokenExpiresAt, arg.RefreshTokenExpiresAt,
arg.CodeHash,
arg.Nonce, arg.Nonce,
arg.UserinfoJson,
) )
var i OidcSession var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
const deleteExpiredOIDCSessions = `-- name: DeleteExpiredOIDCSessions :exec const createOidcUserInfo = `-- name: CreateOidcUserInfo :one
DELETE FROM "oidc_sessions" INSERT INTO "oidc_userinfo" (
WHERE "token_expires_at" < ? AND "refresh_token_expires_at" < ? "sub",
"name",
"preferred_username",
"email",
"groups",
"updated_at",
"given_name",
"family_name",
"middle_name",
"nickname",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"phone_number",
"address"
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING sub, name, preferred_username, email, "groups", updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address
` `
type DeleteExpiredOIDCSessionsParams struct { type CreateOidcUserInfoParams struct {
Sub string
Name string
PreferredUsername string
Email string
Groups string
UpdatedAt int64
GivenName string
FamilyName string
MiddleName string
Nickname string
Profile string
Picture string
Website string
Gender string
Birthdate string
Zoneinfo string
Locale string
PhoneNumber string
Address string
}
func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error) {
row := q.db.QueryRowContext(ctx, createOidcUserInfo,
arg.Sub,
arg.Name,
arg.PreferredUsername,
arg.Email,
arg.Groups,
arg.UpdatedAt,
arg.GivenName,
arg.FamilyName,
arg.MiddleName,
arg.Nickname,
arg.Profile,
arg.Picture,
arg.Website,
arg.Gender,
arg.Birthdate,
arg.Zoneinfo,
arg.Locale,
arg.PhoneNumber,
arg.Address,
)
var i OidcUserinfo
err := row.Scan(
&i.Sub,
&i.Name,
&i.PreferredUsername,
&i.Email,
&i.Groups,
&i.UpdatedAt,
&i.GivenName,
&i.FamilyName,
&i.MiddleName,
&i.Nickname,
&i.Profile,
&i.Picture,
&i.Website,
&i.Gender,
&i.Birthdate,
&i.Zoneinfo,
&i.Locale,
&i.PhoneNumber,
&i.Address,
)
return i, err
}
const deleteExpiredOidcCodes = `-- name: DeleteExpiredOidcCodes :many
DELETE FROM "oidc_codes"
WHERE "expires_at" < ?
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
func (q *Queries) DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]OidcCode, error) {
rows, err := q.db.QueryContext(ctx, deleteExpiredOidcCodes, expiresAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OidcCode
for rows.Next() {
var i OidcCode
if err := rows.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
); 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 deleteExpiredOidcTokens = `-- name: DeleteExpiredOidcTokens :many
DELETE FROM "oidc_tokens"
WHERE "token_expires_at" < ? AND "refresh_token_expires_at" < ?
RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
`
type DeleteExpiredOidcTokensParams struct {
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
} }
func (q *Queries) DeleteExpiredOIDCSessions(ctx context.Context, arg DeleteExpiredOIDCSessionsParams) error { func (q *Queries) DeleteExpiredOidcTokens(ctx context.Context, arg DeleteExpiredOidcTokensParams) ([]OidcToken, error) {
_, err := q.db.ExecContext(ctx, deleteExpiredOIDCSessions, arg.TokenExpiresAt, arg.RefreshTokenExpiresAt) rows, err := q.db.QueryContext(ctx, deleteExpiredOidcTokens, arg.TokenExpiresAt, arg.RefreshTokenExpiresAt)
if err != nil {
return nil, err
}
defer rows.Close()
var items []OidcToken
for rows.Next() {
var i OidcToken
if err := rows.Scan(
&i.Sub,
&i.AccessTokenHash,
&i.RefreshTokenHash,
&i.CodeHash,
&i.Scope,
&i.ClientID,
&i.TokenExpiresAt,
&i.RefreshTokenExpiresAt,
&i.Nonce,
); 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 deleteOidcCode = `-- name: DeleteOidcCode :exec
DELETE FROM "oidc_codes"
WHERE "code_hash" = ?
`
func (q *Queries) DeleteOidcCode(ctx context.Context, codeHash string) error {
_, err := q.db.ExecContext(ctx, deleteOidcCode, codeHash)
return err return err
} }
const deleteOIDCSessionBySub = `-- name: DeleteOIDCSessionBySub :exec const deleteOidcCodeBySub = `-- name: DeleteOidcCodeBySub :exec
DELETE FROM "oidc_sessions" DELETE FROM "oidc_codes"
WHERE "sub" = ? WHERE "sub" = ?
` `
func (q *Queries) DeleteOIDCSessionBySub(ctx context.Context, sub string) error { func (q *Queries) DeleteOidcCodeBySub(ctx context.Context, sub string) error {
_, err := q.db.ExecContext(ctx, deleteOIDCSessionBySub, sub) _, err := q.db.ExecContext(ctx, deleteOidcCodeBySub, sub)
return err return err
} }
const getOIDCSessionByAccessTokenHash = `-- name: GetOIDCSessionByAccessTokenHash :one const deleteOidcToken = `-- name: DeleteOidcToken :exec
SELECT sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json FROM "oidc_sessions" DELETE FROM "oidc_tokens"
WHERE "access_token_hash" = ? WHERE "access_token_hash" = ?
` `
func (q *Queries) GetOIDCSessionByAccessTokenHash(ctx context.Context, accessTokenHash string) (OidcSession, error) { func (q *Queries) DeleteOidcToken(ctx context.Context, accessTokenHash string) error {
row := q.db.QueryRowContext(ctx, getOIDCSessionByAccessTokenHash, accessTokenHash) _, err := q.db.ExecContext(ctx, deleteOidcToken, accessTokenHash)
var i OidcSession return err
}
const deleteOidcTokenByCodeHash = `-- name: DeleteOidcTokenByCodeHash :exec
DELETE FROM "oidc_tokens"
WHERE "code_hash" = ?
`
func (q *Queries) DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error {
_, err := q.db.ExecContext(ctx, deleteOidcTokenByCodeHash, codeHash)
return err
}
const deleteOidcTokenBySub = `-- name: DeleteOidcTokenBySub :exec
DELETE FROM "oidc_tokens"
WHERE "sub" = ?
`
func (q *Queries) DeleteOidcTokenBySub(ctx context.Context, sub string) error {
_, err := q.db.ExecContext(ctx, deleteOidcTokenBySub, sub)
return err
}
const deleteOidcUserInfo = `-- name: DeleteOidcUserInfo :exec
DELETE FROM "oidc_userinfo"
WHERE "sub" = ?
`
func (q *Queries) DeleteOidcUserInfo(ctx context.Context, sub string) error {
_, err := q.db.ExecContext(ctx, deleteOidcUserInfo, sub)
return err
}
const getOidcCode = `-- name: GetOidcCode :one
DELETE FROM "oidc_codes"
WHERE "code_hash" = ?
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
func (q *Queries) GetOidcCode(ctx context.Context, codeHash string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCode, codeHash)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcCodeBySub = `-- name: GetOidcCodeBySub :one
DELETE FROM "oidc_codes"
WHERE "sub" = ?
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
`
func (q *Queries) GetOidcCodeBySub(ctx context.Context, sub string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCodeBySub, sub)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcCodeBySubUnsafe = `-- name: GetOidcCodeBySubUnsafe :one
SELECT sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge FROM "oidc_codes"
WHERE "sub" = ?
`
func (q *Queries) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCodeBySubUnsafe, sub)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcCodeUnsafe = `-- name: GetOidcCodeUnsafe :one
SELECT sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge FROM "oidc_codes"
WHERE "code_hash" = ?
`
func (q *Queries) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (OidcCode, error) {
row := q.db.QueryRowContext(ctx, getOidcCodeUnsafe, codeHash)
var i OidcCode
err := row.Scan(
&i.Sub,
&i.CodeHash,
&i.Scope,
&i.RedirectURI,
&i.ClientID,
&i.ExpiresAt,
&i.Nonce,
&i.CodeChallenge,
)
return i, err
}
const getOidcToken = `-- name: GetOidcToken :one
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
WHERE "access_token_hash" = ?
`
func (q *Queries) GetOidcToken(ctx context.Context, accessTokenHash string) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, getOidcToken, accessTokenHash)
var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
const getOIDCSessionByRefreshTokenHash = `-- name: GetOIDCSessionByRefreshTokenHash :one const getOidcTokenByRefreshToken = `-- name: GetOidcTokenByRefreshToken :one
SELECT sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json FROM "oidc_sessions" SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
WHERE "refresh_token_hash" = ? WHERE "refresh_token_hash" = ?
` `
func (q *Queries) GetOIDCSessionByRefreshTokenHash(ctx context.Context, refreshTokenHash string) (OidcSession, error) { func (q *Queries) GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, getOIDCSessionByRefreshTokenHash, refreshTokenHash) row := q.db.QueryRowContext(ctx, getOidcTokenByRefreshToken, refreshTokenHash)
var i OidcSession var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
const getOIDCSessionBySub = `-- name: GetOIDCSessionBySub :one const getOidcTokenBySub = `-- name: GetOidcTokenBySub :one
SELECT sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json FROM "oidc_sessions" SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
WHERE "sub" = ? WHERE "sub" = ?
` `
func (q *Queries) GetOIDCSessionBySub(ctx context.Context, sub string) (OidcSession, error) { func (q *Queries) GetOidcTokenBySub(ctx context.Context, sub string) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, getOIDCSessionBySub, sub) row := q.db.QueryRowContext(ctx, getOidcTokenBySub, sub)
var i OidcSession var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
const updateOIDCSession = `-- name: UpdateOIDCSession :one const getOidcUserInfo = `-- name: GetOidcUserInfo :one
UPDATE "oidc_sessions" SET SELECT sub, name, preferred_username, email, "groups", updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address FROM "oidc_userinfo"
WHERE "sub" = ?
`
func (q *Queries) GetOidcUserInfo(ctx context.Context, sub string) (OidcUserinfo, error) {
row := q.db.QueryRowContext(ctx, getOidcUserInfo, sub)
var i OidcUserinfo
err := row.Scan(
&i.Sub,
&i.Name,
&i.PreferredUsername,
&i.Email,
&i.Groups,
&i.UpdatedAt,
&i.GivenName,
&i.FamilyName,
&i.MiddleName,
&i.Nickname,
&i.Profile,
&i.Picture,
&i.Website,
&i.Gender,
&i.Birthdate,
&i.Zoneinfo,
&i.Locale,
&i.PhoneNumber,
&i.Address,
)
return i, err
}
const updateOidcTokenByRefreshToken = `-- name: UpdateOidcTokenByRefreshToken :one
UPDATE "oidc_tokens" SET
"access_token_hash" = ?, "access_token_hash" = ?,
"refresh_token_hash" = ?, "refresh_token_hash" = ?,
"scope" = ?,
"client_id" = ?,
"token_expires_at" = ?, "token_expires_at" = ?,
"refresh_token_expires_at" = ?, "refresh_token_expires_at" = ?
"nonce" = ?, WHERE "refresh_token_hash" = ?
"userinfo_json" = ? RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
WHERE "sub" = ?
RETURNING sub, access_token_hash, refresh_token_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce, userinfo_json
` `
type UpdateOIDCSessionParams struct { type UpdateOidcTokenByRefreshTokenParams struct {
AccessTokenHash string AccessTokenHash string
RefreshTokenHash string RefreshTokenHash string
Scope string
ClientID string
TokenExpiresAt int64 TokenExpiresAt int64
RefreshTokenExpiresAt int64 RefreshTokenExpiresAt int64
Nonce string RefreshTokenHash_2 string
UserinfoJson string
Sub string
} }
func (q *Queries) UpdateOIDCSession(ctx context.Context, arg UpdateOIDCSessionParams) (OidcSession, error) { func (q *Queries) UpdateOidcTokenByRefreshToken(ctx context.Context, arg UpdateOidcTokenByRefreshTokenParams) (OidcToken, error) {
row := q.db.QueryRowContext(ctx, updateOIDCSession, row := q.db.QueryRowContext(ctx, updateOidcTokenByRefreshToken,
arg.AccessTokenHash, arg.AccessTokenHash,
arg.RefreshTokenHash, arg.RefreshTokenHash,
arg.Scope,
arg.ClientID,
arg.TokenExpiresAt, arg.TokenExpiresAt,
arg.RefreshTokenExpiresAt, arg.RefreshTokenExpiresAt,
arg.Nonce, arg.RefreshTokenHash_2,
arg.UserinfoJson,
arg.Sub,
) )
var i OidcSession var i OidcToken
err := row.Scan( err := row.Scan(
&i.Sub, &i.Sub,
&i.AccessTokenHash, &i.AccessTokenHash,
&i.RefreshTokenHash, &i.RefreshTokenHash,
&i.CodeHash,
&i.Scope, &i.Scope,
&i.ClientID, &i.ClientID,
&i.TokenExpiresAt, &i.TokenExpiresAt,
&i.RefreshTokenExpiresAt, &i.RefreshTokenExpiresAt,
&i.Nonce, &i.Nonce,
&i.UserinfoJson,
) )
return i, err return i, err
} }
@@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.30.0 // sqlc v1.31.1
// source: session_queries.sql // source: session_queries.sql
package sqlite package sqlite
+120 -24
View File
@@ -32,12 +32,28 @@ func mapErr(err error) error {
return err return err
} }
func (s *Store) CreateOIDCSession(ctx context.Context, arg repository.CreateOIDCSessionParams) (repository.OidcSession, error) { func (s *Store) CreateOidcCode(ctx context.Context, arg repository.CreateOidcCodeParams) (repository.OidcCode, error) {
r, err := s.q.CreateOIDCSession(ctx, CreateOIDCSessionParams(arg)) r, err := s.q.CreateOidcCode(ctx, CreateOidcCodeParams(arg))
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
}
func (s *Store) CreateOidcToken(ctx context.Context, arg repository.CreateOidcTokenParams) (repository.OidcToken, error) {
r, err := s.q.CreateOidcToken(ctx, CreateOidcTokenParams(arg))
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) CreateOidcUserInfo(ctx context.Context, arg repository.CreateOidcUserInfoParams) (repository.OidcUserinfo, error) {
r, err := s.q.CreateOidcUserInfo(ctx, CreateOidcUserInfoParams(arg))
if err != nil {
return repository.OidcUserinfo{}, mapErr(err)
}
return repository.OidcUserinfo(r), nil
} }
func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionParams) (repository.Session, error) { func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionParams) (repository.Session, error) {
@@ -48,44 +64,124 @@ func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionP
return repository.Session(r), nil return repository.Session(r), nil
} }
func (s *Store) DeleteExpiredOIDCSessions(ctx context.Context, arg repository.DeleteExpiredOIDCSessionsParams) error { func (s *Store) DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]repository.OidcCode, error) {
return mapErr(s.q.DeleteExpiredOIDCSessions(ctx, DeleteExpiredOIDCSessionsParams(arg))) rows, err := s.q.DeleteExpiredOidcCodes(ctx, expiresAt)
if err != nil {
return nil, mapErr(err)
}
out := make([]repository.OidcCode, len(rows))
for i, row := range rows {
out[i] = repository.OidcCode(row)
}
return out, nil
}
func (s *Store) DeleteExpiredOidcTokens(ctx context.Context, arg repository.DeleteExpiredOidcTokensParams) ([]repository.OidcToken, error) {
rows, err := s.q.DeleteExpiredOidcTokens(ctx, DeleteExpiredOidcTokensParams(arg))
if err != nil {
return nil, mapErr(err)
}
out := make([]repository.OidcToken, len(rows))
for i, row := range rows {
out[i] = repository.OidcToken(row)
}
return out, nil
} }
func (s *Store) DeleteExpiredSessions(ctx context.Context, expiry int64) error { func (s *Store) DeleteExpiredSessions(ctx context.Context, expiry int64) error {
return mapErr(s.q.DeleteExpiredSessions(ctx, expiry)) return mapErr(s.q.DeleteExpiredSessions(ctx, expiry))
} }
func (s *Store) DeleteOIDCSessionBySub(ctx context.Context, sub string) error { func (s *Store) DeleteOidcCode(ctx context.Context, codeHash string) error {
return mapErr(s.q.DeleteOIDCSessionBySub(ctx, sub)) return mapErr(s.q.DeleteOidcCode(ctx, codeHash))
}
func (s *Store) DeleteOidcCodeBySub(ctx context.Context, sub string) error {
return mapErr(s.q.DeleteOidcCodeBySub(ctx, sub))
}
func (s *Store) DeleteOidcToken(ctx context.Context, accessTokenHash string) error {
return mapErr(s.q.DeleteOidcToken(ctx, accessTokenHash))
}
func (s *Store) DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error {
return mapErr(s.q.DeleteOidcTokenByCodeHash(ctx, codeHash))
}
func (s *Store) DeleteOidcTokenBySub(ctx context.Context, sub string) error {
return mapErr(s.q.DeleteOidcTokenBySub(ctx, sub))
}
func (s *Store) DeleteOidcUserInfo(ctx context.Context, sub string) error {
return mapErr(s.q.DeleteOidcUserInfo(ctx, sub))
} }
func (s *Store) DeleteSession(ctx context.Context, uuid string) error { func (s *Store) DeleteSession(ctx context.Context, uuid string) error {
return mapErr(s.q.DeleteSession(ctx, uuid)) return mapErr(s.q.DeleteSession(ctx, uuid))
} }
func (s *Store) GetOIDCSessionByAccessTokenHash(ctx context.Context, accessTokenHash string) (repository.OidcSession, error) { func (s *Store) GetOidcCode(ctx context.Context, codeHash string) (repository.OidcCode, error) {
r, err := s.q.GetOIDCSessionByAccessTokenHash(ctx, accessTokenHash) r, err := s.q.GetOidcCode(ctx, codeHash)
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
} }
func (s *Store) GetOIDCSessionByRefreshTokenHash(ctx context.Context, refreshTokenHash string) (repository.OidcSession, error) { func (s *Store) GetOidcCodeBySub(ctx context.Context, sub string) (repository.OidcCode, error) {
r, err := s.q.GetOIDCSessionByRefreshTokenHash(ctx, refreshTokenHash) r, err := s.q.GetOidcCodeBySub(ctx, sub)
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
} }
func (s *Store) GetOIDCSessionBySub(ctx context.Context, sub string) (repository.OidcSession, error) { func (s *Store) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (repository.OidcCode, error) {
r, err := s.q.GetOIDCSessionBySub(ctx, sub) r, err := s.q.GetOidcCodeBySubUnsafe(ctx, sub)
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcCode{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcCode(r), nil
}
func (s *Store) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (repository.OidcCode, error) {
r, err := s.q.GetOidcCodeUnsafe(ctx, codeHash)
if err != nil {
return repository.OidcCode{}, mapErr(err)
}
return repository.OidcCode(r), nil
}
func (s *Store) GetOidcToken(ctx context.Context, accessTokenHash string) (repository.OidcToken, error) {
r, err := s.q.GetOidcToken(ctx, accessTokenHash)
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (repository.OidcToken, error) {
r, err := s.q.GetOidcTokenByRefreshToken(ctx, refreshTokenHash)
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) GetOidcTokenBySub(ctx context.Context, sub string) (repository.OidcToken, error) {
r, err := s.q.GetOidcTokenBySub(ctx, sub)
if err != nil {
return repository.OidcToken{}, mapErr(err)
}
return repository.OidcToken(r), nil
}
func (s *Store) GetOidcUserInfo(ctx context.Context, sub string) (repository.OidcUserinfo, error) {
r, err := s.q.GetOidcUserInfo(ctx, sub)
if err != nil {
return repository.OidcUserinfo{}, mapErr(err)
}
return repository.OidcUserinfo(r), nil
} }
func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session, error) { func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session, error) {
@@ -96,12 +192,12 @@ func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session
return repository.Session(r), nil return repository.Session(r), nil
} }
func (s *Store) UpdateOIDCSession(ctx context.Context, arg repository.UpdateOIDCSessionParams) (repository.OidcSession, error) { func (s *Store) UpdateOidcTokenByRefreshToken(ctx context.Context, arg repository.UpdateOidcTokenByRefreshTokenParams) (repository.OidcToken, error) {
r, err := s.q.UpdateOIDCSession(ctx, UpdateOIDCSessionParams(arg)) r, err := s.q.UpdateOidcTokenByRefreshToken(ctx, UpdateOidcTokenByRefreshTokenParams(arg))
if err != nil { if err != nil {
return repository.OidcSession{}, mapErr(err) return repository.OidcToken{}, mapErr(err)
} }
return repository.OidcSession(r), nil return repository.OidcToken(r), nil
} }
func (s *Store) UpdateSession(ctx context.Context, arg repository.UpdateSessionParams) (repository.Session, error) { func (s *Store) UpdateSession(ctx context.Context, arg repository.UpdateSessionParams) (repository.Session, error) {
+25 -8
View File
@@ -19,12 +19,29 @@ type Store interface {
DeleteSession(ctx context.Context, uuid string) error DeleteSession(ctx context.Context, uuid string) error
DeleteExpiredSessions(ctx context.Context, expiry int64) error DeleteExpiredSessions(ctx context.Context, expiry int64) error
// OIDC sessions // OIDC codes
CreateOIDCSession(ctx context.Context, arg CreateOIDCSessionParams) (OidcSession, error) CreateOidcCode(ctx context.Context, arg CreateOidcCodeParams) (OidcCode, error)
DeleteExpiredOIDCSessions(ctx context.Context, arg DeleteExpiredOIDCSessionsParams) error GetOidcCode(ctx context.Context, codeHash string) (OidcCode, error)
DeleteOIDCSessionBySub(ctx context.Context, sub string) error GetOidcCodeBySub(ctx context.Context, sub string) (OidcCode, error)
GetOIDCSessionByAccessTokenHash(ctx context.Context, accessTokenHash string) (OidcSession, error) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (OidcCode, error)
GetOIDCSessionByRefreshTokenHash(ctx context.Context, refreshTokenHash string) (OidcSession, error) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (OidcCode, error)
GetOIDCSessionBySub(ctx context.Context, sub string) (OidcSession, error) DeleteOidcCode(ctx context.Context, codeHash string) error
UpdateOIDCSession(ctx context.Context, arg UpdateOIDCSessionParams) (OidcSession, error) DeleteOidcCodeBySub(ctx context.Context, sub string) error
DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]OidcCode, error)
// OIDC tokens
CreateOidcToken(ctx context.Context, arg CreateOidcTokenParams) (OidcToken, error)
GetOidcToken(ctx context.Context, accessTokenHash string) (OidcToken, error)
GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (OidcToken, error)
GetOidcTokenBySub(ctx context.Context, sub string) (OidcToken, error)
UpdateOidcTokenByRefreshToken(ctx context.Context, arg UpdateOidcTokenByRefreshTokenParams) (OidcToken, error)
DeleteOidcToken(ctx context.Context, accessTokenHash string) error
DeleteOidcTokenBySub(ctx context.Context, sub string) error
DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error
DeleteExpiredOidcTokens(ctx context.Context, arg DeleteExpiredOidcTokensParams) ([]OidcToken, error)
// OIDC userinfo
CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error)
GetOidcUserInfo(ctx context.Context, sub string) (OidcUserinfo, error)
DeleteOidcUserInfo(ctx context.Context, sub string) error
} }
+194 -193
View File
@@ -19,6 +19,7 @@ import (
"slices" "slices"
"github.com/gin-gonic/gin"
"github.com/go-jose/go-jose/v4" "github.com/go-jose/go-jose/v4"
"github.com/steveiliop56/ding" "github.com/steveiliop56/ding"
"github.com/tinyauthapp/tinyauth/internal/model" "github.com/tinyauthapp/tinyauth/internal/model"
@@ -41,10 +42,6 @@ var (
ErrInvalidClient = errors.New("invalid_client") ErrInvalidClient = errors.New("invalid_client")
) )
// This is not spec-compliant, the ID token SHOULD NOT contain user info claims but,
// it has became a "standard" and apps are looking for the claims in the ID tokens
// instead of calling the userinfo endpoint, so we include them in the ID token as well
// for better compatibility with existing apps
type ClaimSet struct { type ClaimSet struct {
Iss string `json:"iss"` Iss string `json:"iss"`
Aud string `json:"aud"` Aud string `json:"aud"`
@@ -70,8 +67,6 @@ type ClaimSet struct {
Nonce string `json:"nonce,omitempty"` Nonce string `json:"nonce,omitempty"`
} }
// We use this struct as both a response struct and a struct to store userinfo
// in the database
type UserinfoResponse struct { type UserinfoResponse struct {
Sub string `json:"sub"` Sub string `json:"sub"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@@ -116,16 +111,6 @@ type AuthorizeRequest struct {
CodeChallengeMethod string `json:"code_challenge_method"` CodeChallengeMethod string `json:"code_challenge_method"`
} }
type AuthorizeCodeEntry struct {
CodeHash string
Scope string
RedirectURI string
ClientID string
Nonce string
CodeChallenge string
Userinfo UserinfoResponse
}
type OIDCService struct { type OIDCService struct {
log *logger.Logger log *logger.Logger
config model.Config config model.Config
@@ -136,10 +121,6 @@ type OIDCService struct {
privateKey *rsa.PrivateKey privateKey *rsa.PrivateKey
publicKey *rsa.PublicKey publicKey *rsa.PublicKey
issuer string issuer string
caches struct {
code *CacheStore[AuthorizeCodeEntry]
}
} }
func NewOIDCService( func NewOIDCService(
@@ -301,26 +282,7 @@ func NewOIDCService(
} }
// Start cleanup routine // Start cleanup routine
// dg.Go(service.cleanupRoutine, ding.RingMinor) dg.Go(service.cleanupRoutine, ding.RingMinor)
// Create caches
codeCash := NewCacheStore[AuthorizeCodeEntry](256)
service.caches.code = codeCash
// Start cache cleanup routine
dg.Go(func(ctx context.Context) {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ticker.C:
service.caches.code.Sweep()
case <-ctx.Done():
return
}
}
}, ding.RingMinor)
return service, nil return service, nil
} }
@@ -383,17 +345,19 @@ func (service *OIDCService) filterScopes(scopes []string) []string {
}) })
} }
func (service *OIDCService) CreateCode(req AuthorizeRequest, userContext model.UserContext) string { func (service *OIDCService) StoreCode(c *gin.Context, sub string, code string, req AuthorizeRequest) error {
code := utils.GenerateString(32) // Fixed 10 minutes
sub := service.CreateSub(userContext, req.ClientID) expiresAt := time.Now().Add(time.Minute * time.Duration(10)).Unix()
entry := AuthorizeCodeEntry{ entry := repository.CreateOidcCodeParams{
CodeHash: service.Hash(code), Sub: sub,
Scope: strings.Join(service.filterScopes(strings.Split(req.Scope, " ")), " "), CodeHash: service.Hash(code),
// Here it's safe to split and trust the output since, we validated the scopes before
Scope: strings.Join(service.filterScopes(strings.Split(req.Scope, " ")), ","),
RedirectURI: req.RedirectURI, RedirectURI: req.RedirectURI,
ClientID: req.ClientID, ClientID: req.ClientID,
ExpiresAt: expiresAt,
Nonce: req.Nonce, Nonce: req.Nonce,
Userinfo: service.userinfoFromContext(userContext, sub),
} }
if req.CodeChallenge != "" { if req.CodeChallenge != "" {
@@ -405,14 +369,14 @@ func (service *OIDCService) CreateCode(req AuthorizeRequest, userContext model.U
} }
} }
// Store the code in the cache // Insert the code into the database
service.caches.code.Set(entry.CodeHash, entry, 10*time.Minute) _, err := service.queries.CreateOidcCode(c, entry)
return code return err
} }
func (service *OIDCService) userinfoFromContext(userContext model.UserContext, sub string) UserinfoResponse { func (service *OIDCService) StoreUserinfo(c *gin.Context, sub string, userContext model.UserContext, req AuthorizeRequest) error {
userInfo := UserinfoResponse{ userInfoParams := repository.CreateOidcUserInfoParams{
Sub: sub, Sub: sub,
Name: userContext.GetName(), Name: userContext.GetName(),
Email: userContext.GetEmail(), Email: userContext.GetEmail(),
@@ -421,31 +385,37 @@ func (service *OIDCService) userinfoFromContext(userContext model.UserContext, s
} }
if userContext.IsLocal() { if userContext.IsLocal() {
userInfo.GivenName = userContext.Local.Attributes.GivenName addressJSON, err := json.Marshal(userContext.Local.Attributes.Address)
userInfo.FamilyName = userContext.Local.Attributes.FamilyName if err != nil {
userInfo.MiddleName = userContext.Local.Attributes.MiddleName return err
userInfo.Nickname = userContext.Local.Attributes.Nickname }
userInfo.Profile = userContext.Local.Attributes.Profile userInfoParams.GivenName = userContext.Local.Attributes.GivenName
userInfo.Picture = userContext.Local.Attributes.Picture userInfoParams.FamilyName = userContext.Local.Attributes.FamilyName
userInfo.Website = userContext.Local.Attributes.Website userInfoParams.MiddleName = userContext.Local.Attributes.MiddleName
userInfo.Gender = userContext.Local.Attributes.Gender userInfoParams.Nickname = userContext.Local.Attributes.Nickname
userInfo.Birthdate = userContext.Local.Attributes.Birthdate userInfoParams.Profile = userContext.Local.Attributes.Profile
userInfo.Zoneinfo = userContext.Local.Attributes.Zoneinfo userInfoParams.Picture = userContext.Local.Attributes.Picture
userInfo.Locale = userContext.Local.Attributes.Locale userInfoParams.Website = userContext.Local.Attributes.Website
userInfo.PhoneNumber = userContext.Local.Attributes.PhoneNumber userInfoParams.Gender = userContext.Local.Attributes.Gender
userInfo.Address = &userContext.Local.Attributes.Address userInfoParams.Birthdate = userContext.Local.Attributes.Birthdate
userInfoParams.Zoneinfo = userContext.Local.Attributes.Zoneinfo
userInfoParams.Locale = userContext.Local.Attributes.Locale
userInfoParams.PhoneNumber = userContext.Local.Attributes.PhoneNumber
userInfoParams.Address = string(addressJSON)
} }
// Tinyauth will pass through the groups it got from an LDAP or an OIDC server // Tinyauth will pass through the groups it got from an LDAP or an OIDC server
if userContext.IsLDAP() { if userContext.IsLDAP() {
userInfo.Groups = userContext.LDAP.Groups userInfoParams.Groups = strings.Join(userContext.LDAP.Groups, ",")
} }
if userContext.IsOAuth() { if userContext.IsOAuth() {
userInfo.Groups = userContext.OAuth.Groups userInfoParams.Groups = strings.Join(userContext.OAuth.Groups, ",")
} }
return userInfo _, err := service.queries.CreateOidcUserInfo(c, userInfoParams)
return err
} }
func (service *OIDCService) ValidateGrantType(grantType string) error { func (service *OIDCService) ValidateGrantType(grantType string) error {
@@ -456,24 +426,36 @@ func (service *OIDCService) ValidateGrantType(grantType string) error {
return nil return nil
} }
func (service *OIDCService) GetCodeEntry(codeHash string, clientId string) (*AuthorizeCodeEntry, bool) { func (service *OIDCService) GetCodeEntry(c *gin.Context, codeHash string, clientId string) (repository.OidcCode, error) {
entry, ok := service.caches.code.Get(codeHash) oidcCode, err := service.queries.GetOidcCode(c, codeHash)
if !ok { if err != nil {
return nil, false if errors.Is(err, repository.ErrNotFound) {
return repository.OidcCode{}, ErrCodeNotFound
}
return repository.OidcCode{}, err
} }
if entry.ClientID != clientId { if time.Now().Unix() > oidcCode.ExpiresAt {
return nil, false err = service.queries.DeleteOidcCode(c, codeHash)
if err != nil {
return repository.OidcCode{}, err
}
err = service.DeleteUserinfo(c, oidcCode.Sub)
if err != nil {
return repository.OidcCode{}, err
}
return repository.OidcCode{}, ErrCodeExpired
} }
// Since the code can only be used once, we delete it from the cache after retrieving it if oidcCode.ClientID != clientId {
service.caches.code.Delete(codeHash) return repository.OidcCode{}, ErrInvalidClient
}
return &entry, true return oidcCode, nil
} }
func (service *OIDCService) generateIDToken(client model.OIDCClientConfig, user UserinfoResponse, scope string, nonce string) (string, error) { func (service *OIDCService) generateIDToken(client model.OIDCClientConfig, user repository.OidcUserinfo, scope string, nonce string) (string, error) {
createdAt := time.Now().Unix() createdAt := time.Now().Unix()
expiresAt := time.Now().Add(time.Duration(service.config.Auth.SessionExpiry) * time.Second).Unix() expiresAt := time.Now().Add(time.Duration(service.config.Auth.SessionExpiry) * time.Second).Unix()
@@ -539,11 +521,17 @@ func (service *OIDCService) generateIDToken(client model.OIDCClientConfig, user
return token, nil return token, nil
} }
func (service *OIDCService) GenerateAccessToken(ctx context.Context, client model.OIDCClientConfig, codeEntry AuthorizeCodeEntry) (*TokenResponse, error) { func (service *OIDCService) GenerateAccessToken(c *gin.Context, client model.OIDCClientConfig, codeEntry repository.OidcCode) (TokenResponse, error) {
idToken, err := service.generateIDToken(client, codeEntry.Userinfo, codeEntry.Scope, codeEntry.Nonce) user, err := service.GetUserinfo(c, codeEntry.Sub)
if err != nil { if err != nil {
return nil, err return TokenResponse{}, err
}
idToken, err := service.generateIDToken(client, user, codeEntry.Scope, codeEntry.Nonce)
if err != nil {
return TokenResponse{}, err
} }
accessToken := utils.GenerateString(32) accessToken := utils.GenerateString(32)
@@ -563,68 +551,56 @@ func (service *OIDCService) GenerateAccessToken(ctx context.Context, client mode
Scope: strings.ReplaceAll(codeEntry.Scope, ",", " "), Scope: strings.ReplaceAll(codeEntry.Scope, ",", " "),
} }
var userInfoJson []byte _, err = service.queries.CreateOidcToken(c, repository.CreateOidcTokenParams{
Sub: codeEntry.Sub,
userInfoJson, err = json.Marshal(codeEntry.Userinfo)
if err != nil {
return nil, err
}
_, err = service.queries.CreateOIDCSession(ctx, repository.CreateOIDCSessionParams{
Sub: codeEntry.Userinfo.Sub,
AccessTokenHash: service.Hash(accessToken), AccessTokenHash: service.Hash(accessToken),
RefreshTokenHash: service.Hash(refreshToken), RefreshTokenHash: service.Hash(refreshToken),
Scope: codeEntry.Scope,
ClientID: client.ClientID, ClientID: client.ClientID,
Scope: codeEntry.Scope,
TokenExpiresAt: tokenExpiresAt, TokenExpiresAt: tokenExpiresAt,
RefreshTokenExpiresAt: refreshTokenExpiresAt, RefreshTokenExpiresAt: refreshTokenExpiresAt,
Nonce: codeEntry.Nonce, Nonce: codeEntry.Nonce,
UserinfoJson: string(userInfoJson), CodeHash: codeEntry.CodeHash,
}) })
if err != nil { if err != nil {
return nil, err return TokenResponse{}, err
} }
return &tokenResponse, nil return tokenResponse, nil
} }
func (service *OIDCService) RefreshAccessToken(ctx context.Context, refreshToken string, clientId string) (*TokenResponse, error) { func (service *OIDCService) RefreshAccessToken(c *gin.Context, refreshToken string, reqClientId string) (TokenResponse, error) {
entry, err := service.queries.GetOIDCSessionByRefreshTokenHash(ctx, service.Hash(refreshToken)) entry, err := service.queries.GetOidcTokenByRefreshToken(c, service.Hash(refreshToken))
if err != nil { if err != nil {
if errors.Is(err, repository.ErrNotFound) { if errors.Is(err, repository.ErrNotFound) {
return nil, ErrTokenNotFound return TokenResponse{}, ErrTokenNotFound
} }
return nil, err return TokenResponse{}, err
} }
if entry.RefreshTokenExpiresAt < time.Now().Unix() { if entry.RefreshTokenExpiresAt < time.Now().Unix() {
return nil, ErrTokenExpired return TokenResponse{}, ErrTokenExpired
} }
// Ensure the client ID in the request matches the client ID in the token // Ensure the client ID in the request matches the client ID in the token
if entry.ClientID != clientId { if entry.ClientID != reqClientId {
return nil, ErrInvalidClient return TokenResponse{}, ErrInvalidClient
} }
// we need to unmarshal the userinfo from the database to include it in the new ID token, user, err := service.GetUserinfo(c, entry.Sub)
// since the ID token includes user claims for better compatibility with existing apps
var userInfo UserinfoResponse
err = json.Unmarshal([]byte(entry.UserinfoJson), &userInfo)
if err != nil { if err != nil {
return nil, err return TokenResponse{}, err
} }
idToken, err := service.generateIDToken(model.OIDCClientConfig{ idToken, err := service.generateIDToken(model.OIDCClientConfig{
ClientID: entry.ClientID, ClientID: entry.ClientID,
}, userInfo, entry.Scope, entry.Nonce) }, user, entry.Scope, entry.Nonce)
if err != nil { if err != nil {
return nil, err return TokenResponse{}, err
} }
accessToken := utils.GenerateString(32) accessToken := utils.GenerateString(32)
@@ -642,54 +618,71 @@ func (service *OIDCService) RefreshAccessToken(ctx context.Context, refreshToken
Scope: strings.ReplaceAll(entry.Scope, ",", " "), Scope: strings.ReplaceAll(entry.Scope, ",", " "),
} }
_, err = service.queries.UpdateOIDCSession(ctx, repository.UpdateOIDCSessionParams{ _, err = service.queries.UpdateOidcTokenByRefreshToken(c, repository.UpdateOidcTokenByRefreshTokenParams{
Sub: entry.Sub,
AccessTokenHash: service.Hash(accessToken), AccessTokenHash: service.Hash(accessToken),
RefreshTokenHash: service.Hash(newRefreshToken), RefreshTokenHash: service.Hash(newRefreshToken),
Scope: entry.Scope,
ClientID: entry.ClientID,
TokenExpiresAt: tokenExpiresAt, TokenExpiresAt: tokenExpiresAt,
RefreshTokenExpiresAt: refreshTokenExpiresAt, RefreshTokenExpiresAt: refreshTokenExpiresAt,
Nonce: entry.Nonce, RefreshTokenHash_2: service.Hash(refreshToken), // that's the selector, it's not stored in the db
UserinfoJson: entry.UserinfoJson,
}) })
if err != nil { if err != nil {
return nil, err return TokenResponse{}, err
} }
return &tokenResponse, nil return tokenResponse, nil
} }
func (service *OIDCService) GetSessionByToken(ctx context.Context, tokenHash string) (*repository.OidcSession, error) { func (service *OIDCService) DeleteCodeEntry(c *gin.Context, codeHash string) error {
entry, err := service.queries.GetOIDCSessionByAccessTokenHash(ctx, tokenHash) return service.queries.DeleteOidcCode(c, codeHash)
}
func (service *OIDCService) DeleteUserinfo(c *gin.Context, sub string) error {
return service.queries.DeleteOidcUserInfo(c, sub)
}
func (service *OIDCService) DeleteToken(c *gin.Context, tokenHash string) error {
return service.queries.DeleteOidcToken(c, tokenHash)
}
func (service *OIDCService) DeleteTokenByCodeHash(c *gin.Context, codeHash string) error {
return service.queries.DeleteOidcTokenByCodeHash(c, codeHash)
}
func (service *OIDCService) GetAccessToken(c *gin.Context, tokenHash string) (repository.OidcToken, error) {
entry, err := service.queries.GetOidcToken(c, tokenHash)
if err != nil { if err != nil {
if errors.Is(err, repository.ErrNotFound) { if errors.Is(err, repository.ErrNotFound) {
return nil, ErrTokenNotFound return repository.OidcToken{}, ErrTokenNotFound
} }
return nil, err return repository.OidcToken{}, err
} }
if entry.TokenExpiresAt < time.Now().Unix() { if entry.TokenExpiresAt < time.Now().Unix() {
// If refresh token is expired, delete the session // If refresh token is expired, delete the token and userinfo since there is no way for the client to access anything anymore
// since there is no way for the client to access anything anymore
if entry.RefreshTokenExpiresAt < time.Now().Unix() { if entry.RefreshTokenExpiresAt < time.Now().Unix() {
// Deletes by sub err := service.DeleteToken(c, tokenHash)
err := service.queries.DeleteSession(ctx, entry.Sub)
if err != nil { if err != nil {
return nil, err return repository.OidcToken{}, err
}
err = service.DeleteUserinfo(c, entry.Sub)
if err != nil {
return repository.OidcToken{}, err
} }
return nil, ErrTokenExpired
} }
return nil, ErrTokenExpired return repository.OidcToken{}, ErrTokenExpired
} }
return &entry, nil return entry, nil
} }
func (service *OIDCService) CompileUserinfo(user UserinfoResponse, scope string) UserinfoResponse { func (service *OIDCService) GetUserinfo(c *gin.Context, sub string) (repository.OidcUserinfo, error) {
scopes := strings.Split(scope, " ") return service.queries.GetOidcUserInfo(c, sub)
}
func (service *OIDCService) CompileUserinfo(user repository.OidcUserinfo, scope string) UserinfoResponse {
scopes := strings.Split(scope, ",") // split by comma since it's a db entry
userInfo := UserinfoResponse{ userInfo := UserinfoResponse{
Sub: user.Sub, Sub: user.Sub,
UpdatedAt: user.UpdatedAt, UpdatedAt: user.UpdatedAt,
@@ -717,7 +710,11 @@ func (service *OIDCService) CompileUserinfo(user UserinfoResponse, scope string)
} }
if slices.Contains(scopes, "groups") { if slices.Contains(scopes, "groups") {
userInfo.Groups = user.Groups if user.Groups != "" {
userInfo.Groups = strings.Split(user.Groups, ",")
} else {
userInfo.Groups = []string{}
}
} }
if slices.Contains(scopes, "phone") { if slices.Contains(scopes, "phone") {
@@ -727,7 +724,10 @@ func (service *OIDCService) CompileUserinfo(user UserinfoResponse, scope string)
} }
if slices.Contains(scopes, "address") { if slices.Contains(scopes, "address") {
userInfo.Address = user.Address var addr model.AddressClaim
if err := json.Unmarshal([]byte(user.Address), &addr); err == nil {
userInfo.Address = &addr
}
} }
return userInfo return userInfo
@@ -740,75 +740,83 @@ func (service *OIDCService) Hash(token string) string {
} }
func (service *OIDCService) DeleteOldSession(ctx context.Context, sub string) error { func (service *OIDCService) DeleteOldSession(ctx context.Context, sub string) error {
err := service.queries.DeleteOIDCSessionBySub(ctx, sub) err := service.queries.DeleteOidcCodeBySub(ctx, sub)
if err != nil && !errors.Is(err, repository.ErrNotFound) {
return err
}
err = service.queries.DeleteOidcTokenBySub(ctx, sub)
if err != nil && !errors.Is(err, repository.ErrNotFound) {
return err
}
err = service.queries.DeleteOidcUserInfo(ctx, sub)
if err != nil && !errors.Is(err, repository.ErrNotFound) { if err != nil && !errors.Is(err, repository.ErrNotFound) {
return err return err
} }
return nil return nil
} }
// // Cleanup routine - Resource heavy due to the linked tables // Cleanup routine - Resource heavy due to the linked tables
// func (service *OIDCService) cleanupRoutine(ctx context.Context) { func (service *OIDCService) cleanupRoutine(ctx context.Context) {
// service.log.App.Debug().Msg("Starting OIDC cleanup routine") service.log.App.Debug().Msg("Starting OIDC cleanup routine")
// ticker := time.NewTicker(time.Duration(30) * time.Minute) ticker := time.NewTicker(time.Duration(30) * time.Minute)
// defer ticker.Stop() defer ticker.Stop()
// for { for {
// select { select {
// case <-ticker.C: case <-ticker.C:
// service.log.App.Debug().Msg("Performing OIDC cleanup routine") service.log.App.Debug().Msg("Performing OIDC cleanup routine")
// currentTime := time.Now().Unix() currentTime := time.Now().Unix()
// // For the OIDC tokens, if they are expired we delete the userinfo and codes // For the OIDC tokens, if they are expired we delete the userinfo and codes
// expiredTokens, err := service.queries.DeleteExpiredOidcTokens(ctx, repository.DeleteExpiredOidcTokensParams{ expiredTokens, err := service.queries.DeleteExpiredOidcTokens(ctx, repository.DeleteExpiredOidcTokensParams{
// TokenExpiresAt: currentTime, TokenExpiresAt: currentTime,
// RefreshTokenExpiresAt: currentTime, RefreshTokenExpiresAt: currentTime,
// }) })
// if err != nil { if err != nil {
// service.log.App.Warn().Err(err).Msg("Failed to delete expired tokens") service.log.App.Warn().Err(err).Msg("Failed to delete expired tokens")
// } }
// for _, expiredToken := range expiredTokens { for _, expiredToken := range expiredTokens {
// err := service.DeleteOldSession(ctx, expiredToken.Sub) err := service.DeleteOldSession(ctx, expiredToken.Sub)
// if err != nil { if err != nil {
// service.log.App.Warn().Err(err).Msg("Failed to delete session for expired token") service.log.App.Warn().Err(err).Msg("Failed to delete session for expired token")
// } }
// } }
// // For expired codes, we need to get the sub, check if tokens are expired and if they are remove everything // For expired codes, we need to get the sub, check if tokens are expired and if they are remove everything
// expiredCodes, err := service.queries.DeleteExpiredOidcCodes(ctx, currentTime) expiredCodes, err := service.queries.DeleteExpiredOidcCodes(ctx, currentTime)
// if err != nil { if err != nil {
// service.log.App.Warn().Err(err).Msg("Failed to delete expired codes") service.log.App.Warn().Err(err).Msg("Failed to delete expired codes")
// } }
// for _, expiredCode := range expiredCodes { for _, expiredCode := range expiredCodes {
// token, err := service.queries.GetOidcTokenBySub(ctx, expiredCode.Sub) token, err := service.queries.GetOidcTokenBySub(ctx, expiredCode.Sub)
// if err != nil { if err != nil {
// if !errors.Is(err, repository.ErrNotFound) { if !errors.Is(err, repository.ErrNotFound) {
// service.log.App.Warn().Err(err).Msg("Failed to get token by sub for expired code") service.log.App.Warn().Err(err).Msg("Failed to get token by sub for expired code")
// } }
// continue continue
// } }
// if token.TokenExpiresAt < currentTime && token.RefreshTokenExpiresAt < currentTime { if token.TokenExpiresAt < currentTime && token.RefreshTokenExpiresAt < currentTime {
// err := service.DeleteOldSession(ctx, expiredCode.Sub) err := service.DeleteOldSession(ctx, expiredCode.Sub)
// if err != nil { if err != nil {
// service.log.App.Warn().Err(err).Msg("Failed to delete session for expired code") service.log.App.Warn().Err(err).Msg("Failed to delete session for expired code")
// } }
// } }
// } }
// service.log.App.Debug().Msg("Finished OIDC cleanup routine") service.log.App.Debug().Msg("Finished OIDC cleanup routine")
// case <-ctx.Done(): case <-ctx.Done():
// service.log.App.Debug().Msg("Stopping OIDC cleanup routine") service.log.App.Debug().Msg("Stopping OIDC cleanup routine")
// return return
// } }
// } }
// } }
func (service *OIDCService) GetJWK() ([]byte, error) { func (service *OIDCService) GetJWK() ([]byte, error) {
hasher := sha256.New() hasher := sha256.New()
@@ -843,10 +851,3 @@ func (service *OIDCService) hashAndEncodePKCE(codeVerifier string) string {
hasher.Write([]byte(codeVerifier)) hasher.Write([]byte(codeVerifier))
return base64.RawURLEncoding.EncodeToString(hasher.Sum(nil)) return base64.RawURLEncoding.EncodeToString(hasher.Sum(nil))
} }
// WARNING: Since Tinyauth is stateless, we cannot have a sub that never changes.
// We will just create a uuid out of the username and client name which remains stable,
// but if username or client name changes then sub changes too.
func (service *OIDCService) CreateSub(userContext model.UserContext, clientId string) string {
return utils.GenerateUUID(fmt.Sprintf("%s:%s", userContext.GetUsername(), clientId))
}
+113 -28
View File
@@ -1,17 +1,46 @@
-- name: GetOIDCSessionBySub :one -- name: CreateOidcCode :one
SELECT * FROM "oidc_sessions" INSERT INTO "oidc_codes" (
"sub",
"code_hash",
"scope",
"redirect_uri",
"client_id",
"expires_at",
"nonce",
"code_challenge"
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8
)
RETURNING *;
-- name: GetOidcCodeUnsafe :one
SELECT * FROM "oidc_codes"
WHERE "code_hash" = $1;
-- name: GetOidcCode :one
DELETE FROM "oidc_codes"
WHERE "code_hash" = $1
RETURNING *;
-- name: GetOidcCodeBySubUnsafe :one
SELECT * FROM "oidc_codes"
WHERE "sub" = $1; WHERE "sub" = $1;
-- name: GetOIDCSessionByAccessTokenHash :one -- name: GetOidcCodeBySub :one
SELECT * FROM "oidc_sessions" DELETE FROM "oidc_codes"
WHERE "access_token_hash" = $1; WHERE "sub" = $1
RETURNING *;
-- name: GetOIDCSessionByRefreshTokenHash :one -- name: DeleteOidcCode :exec
SELECT * FROM "oidc_sessions" DELETE FROM "oidc_codes"
WHERE "refresh_token_hash" = $1; WHERE "code_hash" = $1;
-- name: CreateOIDCSession :one -- name: DeleteOidcCodeBySub :exec
INSERT INTO "oidc_sessions" ( DELETE FROM "oidc_codes"
WHERE "sub" = $1;
-- name: CreateOidcToken :one
INSERT INTO "oidc_tokens" (
"sub", "sub",
"access_token_hash", "access_token_hash",
"refresh_token_hash", "refresh_token_hash",
@@ -19,30 +48,86 @@ INSERT INTO "oidc_sessions" (
"client_id", "client_id",
"token_expires_at", "token_expires_at",
"refresh_token_expires_at", "refresh_token_expires_at",
"nonce", "code_hash",
"userinfo_json" "nonce"
) VALUES ( ) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9 $1, $2, $3, $4, $5, $6, $7, $8, $9
) )
RETURNING *; RETURNING *;
-- name: DeleteOIDCSessionBySub :exec -- name: UpdateOidcTokenByRefreshToken :one
DELETE FROM "oidc_sessions" UPDATE "oidc_tokens" SET
"access_token_hash" = $1,
"refresh_token_hash" = $2,
"token_expires_at" = $3,
"refresh_token_expires_at" = $4
WHERE "refresh_token_hash" = $5
RETURNING *;
-- name: GetOidcToken :one
SELECT * FROM "oidc_tokens"
WHERE "access_token_hash" = $1;
-- name: GetOidcTokenByRefreshToken :one
SELECT * FROM "oidc_tokens"
WHERE "refresh_token_hash" = $1;
-- name: GetOidcTokenBySub :one
SELECT * FROM "oidc_tokens"
WHERE "sub" = $1; WHERE "sub" = $1;
-- name: DeleteExpiredOIDCSessions :exec -- name: DeleteOidcTokenByCodeHash :exec
DELETE FROM "oidc_sessions" DELETE FROM "oidc_tokens"
WHERE "token_expires_at" < $1 AND "refresh_token_expires_at" < $2; WHERE "code_hash" = $1;
-- name: UpdateOIDCSession :one -- name: DeleteOidcToken :exec
UPDATE "oidc_sessions" SET DELETE FROM "oidc_tokens"
"access_token_hash" = $1, WHERE "access_token_hash" = $1;
"refresh_token_hash" = $2,
"scope" = $3, -- name: DeleteOidcTokenBySub :exec
"client_id" = $4, DELETE FROM "oidc_tokens"
"token_expires_at" = $5, WHERE "sub" = $1;
"refresh_token_expires_at" = $6,
"nonce" = $7, -- name: CreateOidcUserInfo :one
"userinfo_json" = $8 INSERT INTO "oidc_userinfo" (
WHERE "sub" = $9 "sub",
"name",
"preferred_username",
"email",
"groups",
"updated_at",
"given_name",
"family_name",
"middle_name",
"nickname",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"phone_number",
"address"
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19
)
RETURNING *;
-- name: GetOidcUserInfo :one
SELECT * FROM "oidc_userinfo"
WHERE "sub" = $1;
-- name: DeleteOidcUserInfo :exec
DELETE FROM "oidc_userinfo"
WHERE "sub" = $1;
-- name: DeleteExpiredOidcCodes :many
DELETE FROM "oidc_codes"
WHERE "expires_at" < $1
RETURNING *;
-- name: DeleteExpiredOidcTokens :many
DELETE FROM "oidc_tokens"
WHERE "token_expires_at" < $1 AND "refresh_token_expires_at" < $2
RETURNING *; RETURNING *;
+43 -10
View File
@@ -1,11 +1,44 @@
CREATE TABLE IF NOT EXISTS "oidc_sessions" ( CREATE TABLE IF NOT EXISTS "oidc_codes" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY, "sub" TEXT NOT NULL UNIQUE,
"access_token_hash" TEXT NOT NULL UNIQUE, "code_hash" TEXT NOT NULL PRIMARY KEY,
"refresh_token_hash" TEXT NOT NULL UNIQUE, "scope" TEXT NOT NULL,
"scope" TEXT NOT NULL, "redirect_uri" TEXT NOT NULL,
"client_id" TEXT NOT NULL, "client_id" TEXT NOT NULL,
"token_expires_at" BIGINT NOT NULL, "expires_at" BIGINT NOT NULL,
"refresh_token_expires_at" BIGINT NOT NULL, "nonce" TEXT NOT NULL DEFAULT '',
"nonce" TEXT NOT NULL DEFAULT '', "code_challenge" TEXT NOT NULL DEFAULT ''
"userinfo_json" TEXT NOT NULL );
CREATE TABLE IF NOT EXISTS "oidc_tokens" (
"sub" TEXT NOT NULL UNIQUE,
"access_token_hash" TEXT NOT NULL PRIMARY KEY,
"refresh_token_hash" TEXT NOT NULL,
"code_hash" TEXT NOT NULL,
"scope" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"token_expires_at" BIGINT NOT NULL,
"refresh_token_expires_at" BIGINT NOT NULL,
"nonce" TEXT NOT NULL DEFAULT ''
);
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
"sub" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"preferred_username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"groups" TEXT NOT NULL,
"updated_at" BIGINT NOT NULL,
"given_name" TEXT NOT NULL,
"family_name" TEXT NOT NULL,
"middle_name" TEXT NOT NULL,
"nickname" TEXT NOT NULL,
"profile" TEXT NOT NULL,
"picture" TEXT NOT NULL,
"website" TEXT NOT NULL,
"gender" TEXT NOT NULL,
"birthdate" TEXT NOT NULL,
"zoneinfo" TEXT NOT NULL,
"locale" TEXT NOT NULL,
"phone_number" TEXT NOT NULL,
"address" TEXT NOT NULL
); );
+113 -28
View File
@@ -1,17 +1,46 @@
-- name: GetOIDCSessionBySub :one -- name: CreateOidcCode :one
SELECT * FROM "oidc_sessions" INSERT INTO "oidc_codes" (
"sub",
"code_hash",
"scope",
"redirect_uri",
"client_id",
"expires_at",
"nonce",
"code_challenge"
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;
-- name: GetOidcCodeUnsafe :one
SELECT * FROM "oidc_codes"
WHERE "code_hash" = ?;
-- name: GetOidcCode :one
DELETE FROM "oidc_codes"
WHERE "code_hash" = ?
RETURNING *;
-- name: GetOidcCodeBySubUnsafe :one
SELECT * FROM "oidc_codes"
WHERE "sub" = ?; WHERE "sub" = ?;
-- name: GetOIDCSessionByAccessTokenHash :one -- name: GetOidcCodeBySub :one
SELECT * FROM "oidc_sessions" DELETE FROM "oidc_codes"
WHERE "access_token_hash" = ?; WHERE "sub" = ?
RETURNING *;
-- name: GetOIDCSessionByRefreshTokenHash :one -- name: DeleteOidcCode :exec
SELECT * FROM "oidc_sessions" DELETE FROM "oidc_codes"
WHERE "refresh_token_hash" = ?; WHERE "code_hash" = ?;
-- name: CreateOIDCSession :one -- name: DeleteOidcCodeBySub :exec
INSERT INTO "oidc_sessions" ( DELETE FROM "oidc_codes"
WHERE "sub" = ?;
-- name: CreateOidcToken :one
INSERT INTO "oidc_tokens" (
"sub", "sub",
"access_token_hash", "access_token_hash",
"refresh_token_hash", "refresh_token_hash",
@@ -19,30 +48,86 @@ INSERT INTO "oidc_sessions" (
"client_id", "client_id",
"token_expires_at", "token_expires_at",
"refresh_token_expires_at", "refresh_token_expires_at",
"nonce", "code_hash",
"userinfo_json" "nonce"
) VALUES ( ) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ? ?, ?, ?, ?, ?, ?, ?, ?, ?
) )
RETURNING *; RETURNING *;
-- name: DeleteOIDCSessionBySub :exec -- name: UpdateOidcTokenByRefreshToken :one
DELETE FROM "oidc_sessions" UPDATE "oidc_tokens" SET
WHERE "sub" = ?;
-- name: DeleteExpiredOIDCSessions :exec
DELETE FROM "oidc_sessions"
WHERE "token_expires_at" < ? AND "refresh_token_expires_at" < ?;
-- name: UpdateOIDCSession :one
UPDATE "oidc_sessions" SET
"access_token_hash" = ?, "access_token_hash" = ?,
"refresh_token_hash" = ?, "refresh_token_hash" = ?,
"scope" = ?,
"client_id" = ?,
"token_expires_at" = ?, "token_expires_at" = ?,
"refresh_token_expires_at" = ?, "refresh_token_expires_at" = ?
"nonce" = ?, WHERE "refresh_token_hash" = ?
"userinfo_json" = ? RETURNING *;
WHERE "sub" = ?
-- name: GetOidcToken :one
SELECT * FROM "oidc_tokens"
WHERE "access_token_hash" = ?;
-- name: GetOidcTokenByRefreshToken :one
SELECT * FROM "oidc_tokens"
WHERE "refresh_token_hash" = ?;
-- name: GetOidcTokenBySub :one
SELECT * FROM "oidc_tokens"
WHERE "sub" = ?;
-- name: DeleteOidcTokenByCodeHash :exec
DELETE FROM "oidc_tokens"
WHERE "code_hash" = ?;
-- name: DeleteOidcToken :exec
DELETE FROM "oidc_tokens"
WHERE "access_token_hash" = ?;
-- name: DeleteOidcTokenBySub :exec
DELETE FROM "oidc_tokens"
WHERE "sub" = ?;
-- name: CreateOidcUserInfo :one
INSERT INTO "oidc_userinfo" (
"sub",
"name",
"preferred_username",
"email",
"groups",
"updated_at",
"given_name",
"family_name",
"middle_name",
"nickname",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"phone_number",
"address"
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
RETURNING *;
-- name: GetOidcUserInfo :one
SELECT * FROM "oidc_userinfo"
WHERE "sub" = ?;
-- name: DeleteOidcUserInfo :exec
DELETE FROM "oidc_userinfo"
WHERE "sub" = ?;
-- name: DeleteExpiredOidcCodes :many
DELETE FROM "oidc_codes"
WHERE "expires_at" < ?
RETURNING *;
-- name: DeleteExpiredOidcTokens :many
DELETE FROM "oidc_tokens"
WHERE "token_expires_at" < ? AND "refresh_token_expires_at" < ?
RETURNING *; RETURNING *;
+39 -6
View File
@@ -1,11 +1,44 @@
CREATE TABLE IF NOT EXISTS "oidc_sessions" ( CREATE TABLE IF NOT EXISTS "oidc_codes" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY, "sub" TEXT NOT NULL UNIQUE,
"access_token_hash" TEXT NOT NULL UNIQUE, "code_hash" TEXT NOT NULL PRIMARY KEY UNIQUE,
"refresh_token_hash" TEXT NOT NULL UNIQUE, "scope" TEXT NOT NULL,
"redirect_uri" TEXT NOT NULL,
"client_id" TEXT NOT NULL,
"expires_at" INTEGER NOT NULL,
"nonce" TEXT DEFAULT "",
"code_challenge" TEXT DEFAULT ""
);
CREATE TABLE IF NOT EXISTS "oidc_tokens" (
"sub" TEXT NOT NULL UNIQUE,
"access_token_hash" TEXT NOT NULL PRIMARY KEY UNIQUE,
"refresh_token_hash" TEXT NOT NULL,
"code_hash" TEXT NOT NULL,
"scope" TEXT NOT NULL, "scope" TEXT NOT NULL,
"client_id" TEXT NOT NULL, "client_id" TEXT NOT NULL,
"token_expires_at" INTEGER NOT NULL, "token_expires_at" INTEGER NOT NULL,
"refresh_token_expires_at" INTEGER NOT NULL, "refresh_token_expires_at" INTEGER NOT NULL,
"nonce" TEXT DEFAULT "", "nonce" TEXT DEFAULT ""
"userinfo_json" TEXT NOT NULL );
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
"sub" TEXT NOT NULL UNIQUE PRIMARY KEY,
"name" TEXT NOT NULL,
"preferred_username" TEXT NOT NULL,
"email" TEXT NOT NULL,
"groups" TEXT NOT NULL,
"updated_at" INTEGER NOT NULL,
"given_name" TEXT NOT NULL,
"family_name" TEXT NOT NULL,
"middle_name" TEXT NOT NULL,
"nickname" TEXT NOT NULL,
"profile" TEXT NOT NULL,
"picture" TEXT NOT NULL,
"website" TEXT NOT NULL,
"gender" TEXT NOT NULL,
"birthdate" TEXT NOT NULL,
"zoneinfo" TEXT NOT NULL,
"locale" TEXT NOT NULL,
"phone_number" TEXT NOT NULL,
"address" TEXT NOT NULL
); );
+5 -1
View File
@@ -22,7 +22,11 @@ sql:
go_type: "string" go_type: "string"
- column: "sessions.ldap_groups" - column: "sessions.ldap_groups"
go_type: "string" go_type: "string"
- column: "oidc_sessions.nonce" - column: "oidc_codes.nonce"
go_type: "string"
- column: "oidc_tokens.nonce"
go_type: "string"
- column: "oidc_codes.code_challenge"
go_type: "string" go_type: "string"
- engine: "postgresql" - engine: "postgresql"
queries: "sql/postgres/*_queries.sql" queries: "sql/postgres/*_queries.sql"