feat(storage): only store identity token metadata (#2627)

This change makes it so only metadata about tokens is stored. Tokens can still be resigned due to conversion methods that convert from the JWT type to the database type. This should be more efficient and should mean we don't have to encrypt tokens or token info in the database at least for now.
pull/2646/head
James Elliott 2021-11-30 17:58:21 +11:00 committed by GitHub
parent b1d37d2069
commit 9ceee6c660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 170 additions and 128 deletions

View File

@ -11,8 +11,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/mocks"
"github.com/authelia/authelia/v4/internal/models"
)
type HandlerRegisterU2FStep1Suite struct {
@ -34,34 +34,30 @@ func (s *HandlerRegisterU2FStep1Suite) TearDownTest() {
s.mock.Close()
}
func createToken(secret string, username string, action string, expiresAt time.Time) string {
claims := &middlewares.IdentityVerificationClaim{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: &jwt.NumericDate{
Time: expiresAt,
},
Issuer: "Authelia",
},
Action: action,
Username: username,
}
func createToken(secret, username, action string, expiresAt time.Time) (data string, verification models.IdentityVerification) {
verification = models.NewIdentityVerification(username, action)
verification.ExpiresAt = expiresAt
claims := verification.ToIdentityVerificationClaim()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, _ := token.SignedString([]byte(secret))
return ss
return ss, verification
}
func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedProtoIsMissing() {
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", ActionU2FRegistration,
token, v := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", ActionU2FRegistration,
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(v.JTI.String())).
Return(true, nil)
s.mock.StorageProviderMock.EXPECT().
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(v.JTI.String())).
Return(nil)
SecondFactorU2FIdentityFinish(s.mock.Ctx)
@ -72,16 +68,16 @@ func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedProtoIsMissi
func (s *HandlerRegisterU2FStep1Suite) TestShouldRaiseWhenXForwardedHostIsMissing() {
s.mock.Ctx.Request.Header.Add("X-Forwarded-Proto", "http")
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", ActionU2FRegistration,
token, v := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", ActionU2FRegistration,
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(v.JTI.String())).
Return(true, nil)
s.mock.StorageProviderMock.EXPECT().
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(v.JTI.String())).
Return(nil)
SecondFactorU2FIdentityFinish(s.mock.Ctx)

View File

@ -1,7 +1,5 @@
package middlewares
const jwtIssuer = "Authelia"
const (
headerXForwardedProto = "X-Forwarded-Proto"
headerXForwardedMethod = "X-Forwarded-Method"

View File

@ -4,7 +4,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
@ -20,7 +19,6 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs) RequestHandle
return func(ctx *AutheliaCtx) {
identity, err := args.IdentityRetrieverFunc(ctx)
if err != nil {
// In that case we reply ok to avoid user enumeration.
ctx.Logger.Error(err)
@ -29,17 +27,11 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs) RequestHandle
return
}
verification := models.NewIdentityVerification(identity.Username, args.ActionClaim)
// Create the claim with the action to sign it.
claims := &IdentityVerificationClaim{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: &jwt.NumericDate{
Time: time.Now().Add(5 * time.Minute),
},
Issuer: jwtIssuer,
},
Action: args.ActionClaim,
Username: identity.Username,
}
claims := verification.ToIdentityVerificationClaim()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString([]byte(ctx.Configuration.JWTSecret))
@ -48,9 +40,7 @@ func IdentityVerificationStart(args IdentityVerificationStartArgs) RequestHandle
return
}
err = ctx.Providers.StorageProvider.SaveIdentityVerification(ctx, models.IdentityVerification{
Token: ss,
})
err = ctx.Providers.StorageProvider.SaveIdentityVerification(ctx, verification)
if err != nil {
ctx.Error(err, messageOperationFailed)
return
@ -131,20 +121,7 @@ func IdentityVerificationFinish(args IdentityVerificationFinishArgs, next func(c
return
}
found, err := ctx.Providers.StorageProvider.FindIdentityVerification(ctx, finishBody.Token)
if err != nil {
ctx.Error(err, messageOperationFailed)
return
}
if !found {
ctx.Error(fmt.Errorf("Token is not in DB, it might have already been used"),
messageIdentityVerificationTokenAlreadyUsed)
return
}
token, err := jwt.ParseWithClaims(finishBody.Token, &IdentityVerificationClaim{},
token, err := jwt.ParseWithClaims(finishBody.Token, &models.IdentityVerificationClaim{},
func(token *jwt.Token) (interface{}, error) {
return []byte(ctx.Configuration.JWTSecret), nil
})
@ -170,12 +147,31 @@ func IdentityVerificationFinish(args IdentityVerificationFinishArgs, next func(c
return
}
claims, ok := token.Claims.(*IdentityVerificationClaim)
claims, ok := token.Claims.(*models.IdentityVerificationClaim)
if !ok {
ctx.Error(fmt.Errorf("Wrong type of claims (%T != *middlewares.IdentityVerificationClaim)", claims), messageOperationFailed)
return
}
verification, err := claims.ToIdentityVerification()
if err != nil {
ctx.Error(fmt.Errorf("Token seems to be invalid: %w", err),
messageOperationFailed)
return
}
found, err := ctx.Providers.StorageProvider.FindIdentityVerification(ctx, verification.JTI.String())
if err != nil {
ctx.Error(err, messageOperationFailed)
return
}
if !found {
ctx.Error(fmt.Errorf("Token is not in DB, it might have already been used"),
messageIdentityVerificationTokenAlreadyUsed)
return
}
// Verify that the action claim in the token is the one expected for the given endpoint.
if claims.Action != args.ActionClaim {
ctx.Error(fmt.Errorf("This token has not been generated for this kind of action"), messageOperationFailed)
@ -187,8 +183,7 @@ func IdentityVerificationFinish(args IdentityVerificationFinishArgs, next func(c
return
}
// TODO(c.michaud): find a way to garbage collect unused tokens.
err = ctx.Providers.StorageProvider.RemoveIdentityVerification(ctx, finishBody.Token)
err = ctx.Providers.StorageProvider.RemoveIdentityVerification(ctx, claims.ID)
if err != nil {
ctx.Error(err, messageOperationFailed)
return

View File

@ -12,6 +12,7 @@ import (
"github.com/authelia/authelia/v4/internal/middlewares"
"github.com/authelia/authelia/v4/internal/mocks"
"github.com/authelia/authelia/v4/internal/models"
"github.com/authelia/authelia/v4/internal/session"
)
@ -164,21 +165,17 @@ func (s *IdentityVerificationFinishProcess) TearDownTest() {
s.mock.Close()
}
func createToken(secret string, username string, action string, expiresAt time.Time) string {
claims := &middlewares.IdentityVerificationClaim{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: &jwt.NumericDate{
Time: expiresAt,
},
Issuer: "Authelia",
},
Action: action,
Username: username,
}
func createToken(secret, username, action string, expiresAt time.Time) (data string, verification models.IdentityVerification) {
verification = models.NewIdentityVerification(username, action)
verification.ExpiresAt = expiresAt
claims := verification.ToIdentityVerificationClaim()
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, _ := token.SignedString([]byte(secret))
return ss
return ss, verification
}
func next(ctx *middlewares.AutheliaCtx, username string) {}
@ -206,10 +203,13 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenIsNotProvided()
}
func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenIsNotFoundInDB() {
s.mock.Ctx.Request.SetBodyString("{\"token\":\"abc\"}")
token, verification := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", "Login",
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq("abc")).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(false, nil)
middlewares.IdentityVerificationFinish(newFinishArgs(), next)(s.mock.Ctx)
@ -221,10 +221,6 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenIsNotFoundInDB(
func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenIsInvalid() {
s.mock.Ctx.Request.SetBodyString("{\"token\":\"abc\"}")
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq("abc")).
Return(true, nil)
middlewares.IdentityVerificationFinish(newFinishArgs(), next)(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "Operation failed")
@ -233,14 +229,10 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenIsInvalid() {
func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenExpired() {
args := newArgs(defaultRetriever)
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", args.ActionClaim,
token, _ := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", args.ActionClaim,
time.Now().Add(-1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
Return(true, nil)
middlewares.IdentityVerificationFinish(newFinishArgs(), next)(s.mock.Ctx)
s.mock.Assert200KO(s.T(), "The identity verification token has expired")
@ -248,12 +240,12 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenExpired() {
}
func (s *IdentityVerificationFinishProcess) TestShouldFailForWrongAction() {
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "", "",
token, verification := createToken(s.mock.Ctx.Configuration.JWTSecret, "", "",
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(true, nil)
middlewares.IdentityVerificationFinish(newFinishArgs(), next)(s.mock.Ctx)
@ -263,12 +255,12 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailForWrongAction() {
}
func (s *IdentityVerificationFinishProcess) TestShouldFailForWrongUser() {
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "harry", "EXP_ACTION",
token, verification := createToken(s.mock.Ctx.Configuration.JWTSecret, "harry", "EXP_ACTION",
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(true, nil)
args := newFinishArgs()
@ -280,16 +272,16 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailForWrongUser() {
}
func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenCannotBeRemovedFromDB() {
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", "EXP_ACTION",
token, verification := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", "EXP_ACTION",
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(true, nil)
s.mock.StorageProviderMock.EXPECT().
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(fmt.Errorf("cannot remove"))
middlewares.IdentityVerificationFinish(newFinishArgs(), next)(s.mock.Ctx)
@ -299,16 +291,16 @@ func (s *IdentityVerificationFinishProcess) TestShouldFailIfTokenCannotBeRemoved
}
func (s *IdentityVerificationFinishProcess) TestShouldReturn200OnFinishComplete() {
token := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", "EXP_ACTION",
token, verification := createToken(s.mock.Ctx.Configuration.JWTSecret, "john", "EXP_ACTION",
time.Now().Add(1*time.Minute))
s.mock.Ctx.Request.SetBodyString(fmt.Sprintf("{\"token\":\"%s\"}", token))
s.mock.StorageProviderMock.EXPECT().
FindIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
FindIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(true, nil)
s.mock.StorageProviderMock.EXPECT().
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(token)).
RemoveIdentityVerification(s.mock.Ctx, gomock.Eq(verification.JTI.String())).
Return(nil)
middlewares.IdentityVerificationFinish(newFinishArgs(), next)(s.mock.Ctx)

View File

@ -1,7 +1,6 @@
package middlewares
import (
"github.com/golang-jwt/jwt/v4"
"github.com/sirupsen/logrus"
"github.com/valyala/fasthttp"
@ -80,17 +79,6 @@ type IdentityVerificationFinishArgs struct {
IsTokenUserValidFunc func(ctx *AutheliaCtx, username string) bool
}
// IdentityVerificationClaim custom claim for specifying the action claim.
// The action can be to register a TOTP device, a U2F device or reset one's password.
type IdentityVerificationClaim struct {
jwt.RegisteredClaims
// The action this token has been crafted for.
Action string `json:"action"`
// The user this token has been crafted for.
Username string `json:"username"`
}
// IdentityVerificationFinishBody type of the body received by the finish endpoint.
type IdentityVerificationFinishBody struct {
Token string `json:"token"`

View File

@ -2,11 +2,69 @@ package models
import (
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
)
// NewIdentityVerification creates a new IdentityVerification from a given username and action.
func NewIdentityVerification(username, action string) (verification IdentityVerification) {
return IdentityVerification{
JTI: uuid.New(),
IssuedAt: time.Now(),
ExpiresAt: time.Now().Add(5 * time.Minute),
Action: action,
Username: username,
}
}
// IdentityVerification represents an identity verification row in the database.
type IdentityVerification struct {
ID int `db:"id"`
Created time.Time `db:"created"`
Token string `db:"token"`
JTI uuid.UUID `db:"jti"`
IssuedAt time.Time `db:"iat"`
ExpiresAt time.Time `db:"exp"`
Used *time.Time `db:"used"`
Action string `db:"action"`
Username string `db:"username"`
}
// ToIdentityVerificationClaim converts the IdentityVerification into a IdentityVerificationClaim.
func (v IdentityVerification) ToIdentityVerificationClaim() (claim *IdentityVerificationClaim) {
return &IdentityVerificationClaim{
RegisteredClaims: jwt.RegisteredClaims{
ID: v.JTI.String(),
Issuer: "Authelia",
IssuedAt: jwt.NewNumericDate(v.IssuedAt),
ExpiresAt: jwt.NewNumericDate(v.ExpiresAt),
},
Action: v.Action,
Username: v.Username,
}
}
// IdentityVerificationClaim custom claim for specifying the action claim.
// The action can be to register a TOTP device, a U2F device or reset one's password.
type IdentityVerificationClaim struct {
jwt.RegisteredClaims
// The action this token has been crafted for.
Action string `json:"action"`
// The user this token has been crafted for.
Username string `json:"username"`
}
// ToIdentityVerification converts the IdentityVerificationClaim into a IdentityVerification.
func (v IdentityVerificationClaim) ToIdentityVerification() (verification *IdentityVerification, err error) {
jti, err := uuid.Parse(v.ID)
if err != nil {
return nil, err
}
return &IdentityVerification{
JTI: jti,
Username: v.Username,
Action: v.Action,
ExpiresAt: v.ExpiresAt.Time,
}, nil
}

View File

@ -6,7 +6,7 @@ import (
const (
tableUserPreferences = "user_preferences"
tableIdentityVerification = "identity_verification_tokens"
tableIdentityVerification = "identity_verification"
tableTOTPConfigurations = "totp_configurations"
tableU2FDevices = "u2f_devices"
tableDUODevices = "duo_devices"

View File

@ -1,5 +1,5 @@
DROP TABLE IF EXISTS authentication_logs;
DROP TABLE IF EXISTS identity_verification_tokens;
DROP TABLE IF EXISTS identity_verification;
DROP TABLE IF EXISTS totp_configurations;
DROP TABLE IF EXISTS u2f_devices;
DROP TABLE IF EXISTS user_preferences;

View File

@ -14,12 +14,16 @@ CREATE TABLE IF NOT EXISTS authentication_logs (
CREATE INDEX authentication_logs_username_idx ON authentication_logs (time, username, auth_type);
CREATE INDEX authentication_logs_remote_ip_idx ON authentication_logs (time, remote_ip, auth_type);
CREATE TABLE IF NOT EXISTS identity_verification_tokens (
CREATE TABLE IF NOT EXISTS identity_verification (
id INTEGER AUTO_INCREMENT,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
token VARCHAR(512),
jti CHAR(36),
iat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
exp TIMESTAMP NOT NULL,
used TIMESTAMP NULL DEFAULT NULL,
username VARCHAR(100) NOT NULL,
action VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (token)
UNIQUE KEY (jti)
);
CREATE TABLE IF NOT EXISTS totp_configurations (

View File

@ -14,12 +14,16 @@ CREATE TABLE IF NOT EXISTS authentication_logs (
CREATE INDEX authentication_logs_username_idx ON authentication_logs (time, username, auth_type);
CREATE INDEX authentication_logs_remote_ip_idx ON authentication_logs (time, remote_ip, auth_type);
CREATE TABLE IF NOT EXISTS identity_verification_tokens (
CREATE TABLE IF NOT EXISTS identity_verification (
id SERIAL,
created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
token VARCHAR(512),
jti CHAR(36),
iat TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
exp TIMESTAMP WITH TIME ZONE NOT NULL,
used TIMESTAMP WITH TIME ZONE NULL DEFAULT NULL,
username VARCHAR(100) NOT NULL,
action VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
UNIQUE (token)
UNIQUE (jti)
);
CREATE TABLE IF NOT EXISTS totp_configurations (

View File

@ -14,12 +14,16 @@ CREATE TABLE IF NOT EXISTS authentication_logs (
CREATE INDEX authentication_logs_username_idx ON authentication_logs (time, username, auth_type);
CREATE INDEX authentication_logs_remote_ip_idx ON authentication_logs (time, remote_ip, auth_type);
CREATE TABLE IF NOT EXISTS identity_verification_tokens (
CREATE TABLE IF NOT EXISTS identity_verification (
id INTEGER,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
token VARCHAR(512),
jti VARCHAR(36),
iat TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
exp TIMESTAMP NOT NULL,
used TIMESTAMP NULL DEFAULT NULL,
username VARCHAR(100) NOT NULL,
action VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
UNIQUE (token)
UNIQUE (jti)
);
CREATE TABLE IF NOT EXISTS totp_configurations (

View File

@ -81,7 +81,7 @@ type SQLProvider struct {
sqlInsertAuthenticationAttempt string
sqlSelectAuthenticationAttemptsByUsername string
// Table: identity_verification_tokens.
// Table: identity_verification.
sqlInsertIdentityVerification string
sqlDeleteIdentityVerification string
sqlSelectExistsIdentityVerification string
@ -208,7 +208,9 @@ func (p *SQLProvider) LoadUserInfo(ctx context.Context, username string) (info m
// SaveIdentityVerification save an identity verification record to the database.
func (p *SQLProvider) SaveIdentityVerification(ctx context.Context, verification models.IdentityVerification) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlInsertIdentityVerification, verification.Token); err != nil {
if _, err = p.db.ExecContext(ctx, p.sqlInsertIdentityVerification,
verification.JTI, verification.IssuedAt, verification.ExpiresAt,
verification.Username, verification.Action); err != nil {
return fmt.Errorf("error inserting identity verification: %w", err)
}
@ -216,8 +218,8 @@ func (p *SQLProvider) SaveIdentityVerification(ctx context.Context, verification
}
// RemoveIdentityVerification remove an identity verification record from the database.
func (p *SQLProvider) RemoveIdentityVerification(ctx context.Context, token string) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlDeleteIdentityVerification, token); err != nil {
func (p *SQLProvider) RemoveIdentityVerification(ctx context.Context, jti string) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlDeleteIdentityVerification, jti); err != nil {
return fmt.Errorf("error updating identity verification: %w", err)
}
@ -225,8 +227,8 @@ func (p *SQLProvider) RemoveIdentityVerification(ctx context.Context, token stri
}
// FindIdentityVerification checks if an identity verification record is in the database and active.
func (p *SQLProvider) FindIdentityVerification(ctx context.Context, token string) (found bool, err error) {
if err = p.db.GetContext(ctx, &found, p.sqlSelectExistsIdentityVerification, token); err != nil {
func (p *SQLProvider) FindIdentityVerification(ctx context.Context, jti string) (found bool, err error) {
if err = p.db.GetContext(ctx, &found, p.sqlSelectExistsIdentityVerification, jti); err != nil {
return false, fmt.Errorf("error selecting identity verification exists: %w", err)
}

View File

@ -60,16 +60,17 @@ const (
SELECT EXISTS (
SELECT id
FROM %s
WHERE token = ?
WHERE jti = ? AND exp > CURRENT_TIMESTAMP AND used IS NULL
);`
queryFmtInsertIdentityVerification = `
INSERT INTO %s (token)
VALUES (?);`
INSERT INTO %s (jti, iat, exp, username, action)
VALUES (?, ?, ?, ?, ?);`
queryFmtDeleteIdentityVerification = `
DELETE FROM %s
WHERE token = ?;`
UPDATE %s
SET used = CURRENT_TIMESTAMP
WHERE jti = ?;`
)
const (

View File

@ -261,7 +261,7 @@ func (s *CLISuite) TestStorage02ShouldShowSchemaInfo() {
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "schema-info", "--config", "/config/configuration.storage.yml"})
s.Assert().NoError(err)
pattern := regexp.MustCompile(`^Schema Version: \d+\nSchema Upgrade Available: no\nSchema Tables: authentication_logs, identity_verification_tokens, totp_configurations, u2f_devices, user_preferences, migrations, encryption\nSchema Encryption Key: valid`)
pattern := regexp.MustCompile(`^Schema Version: \d+\nSchema Upgrade Available: no\nSchema Tables: authentication_logs, identity_verification, totp_configurations, u2f_devices, user_preferences, migrations, encryption\nSchema Encryption Key: valid`)
s.Assert().Regexp(pattern, output)
}
@ -336,7 +336,7 @@ func (s *CLISuite) TestStorage04ShouldChangeEncryptionKey() {
output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "schema-info", "--config", "/config/configuration.storage.yml"})
s.Assert().NoError(err)
pattern := regexp.MustCompile(`Schema Version: \d+\nSchema Upgrade Available: no\nSchema Tables: authentication_logs, identity_verification_tokens, totp_configurations, u2f_devices, user_preferences, migrations, encryption\nSchema Encryption Key: invalid`)
pattern := regexp.MustCompile(`Schema Version: \d+\nSchema Upgrade Available: no\nSchema Tables: authentication_logs, identity_verification, totp_configurations, u2f_devices, user_preferences, migrations, encryption\nSchema Encryption Key: invalid`)
s.Assert().Regexp(pattern, output)
output, err = s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "storage", "encryption", "check", "--config", "/config/configuration.storage.yml"})