Callbacks service docs updated
This commit is contained in:
@@ -12,4 +12,7 @@ type DB interface {
|
||||
auth.ProtectedDB[*model.Callback]
|
||||
SetArchived(ctx context.Context, accountRef, organizationRef, callbackRef bson.ObjectID, archived, cascade bool) error
|
||||
List(ctx context.Context, accountRef, organizationRef, _ bson.ObjectID, cursor *model.ViewCursor) ([]model.Callback, error)
|
||||
GetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) (string, error)
|
||||
SetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID, secretRef string) error
|
||||
ClearSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) error
|
||||
}
|
||||
|
||||
8
api/pkg/db/internal/mongo/callbacksdb/callback.go
Normal file
8
api/pkg/db/internal/mongo/callbacksdb/callback.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package callbacksdb
|
||||
|
||||
import "github.com/tech/sendico/pkg/model"
|
||||
|
||||
type callbackInternal struct {
|
||||
model.Callback `bson:",inline" json:",inline"`
|
||||
SecretRef string `bson:"secret_ref" json:"-"`
|
||||
}
|
||||
@@ -2,14 +2,12 @@ package callbacksdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/tech/sendico/pkg/auth"
|
||||
"github.com/tech/sendico/pkg/db/callbacks"
|
||||
"github.com/tech/sendico/pkg/db/policy"
|
||||
ri "github.com/tech/sendico/pkg/db/repository/index"
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
@@ -29,10 +27,6 @@ func Create(
|
||||
pdb policy.DB,
|
||||
db *mongo.Database,
|
||||
) (*CallbacksDB, error) {
|
||||
if err := ensureBuiltInPolicy(ctx, logger, pdb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := auth.CreateDBImp[*model.Callback](ctx, logger, pdb, enforcer, mservice.Callbacks, db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -43,7 +37,7 @@ func Create(
|
||||
Name: "uq_callbacks_client_url",
|
||||
Keys: []ri.Key{
|
||||
{Field: storable.OrganizationRefField, Sort: ri.Asc},
|
||||
{Field: "client_id", Sort: ri.Asc},
|
||||
{Field: "organization_ref", Sort: ri.Asc},
|
||||
{Field: "url", Sort: ri.Asc},
|
||||
},
|
||||
Unique: true,
|
||||
@@ -82,31 +76,4 @@ func Create(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ensureBuiltInPolicy(ctx context.Context, logger mlogger.Logger, pdb policy.DB) error {
|
||||
var existing model.PolicyDescription
|
||||
if err := pdb.GetBuiltInPolicy(ctx, mservice.Callbacks, &existing); err == nil {
|
||||
return nil
|
||||
} else if !errors.Is(err, merrors.ErrNoData) {
|
||||
return err
|
||||
}
|
||||
|
||||
description := "Callbacks subscription management"
|
||||
resourceTypes := []mservice.Type{mservice.Callbacks}
|
||||
policyDescription := &model.PolicyDescription{
|
||||
Describable: model.Describable{
|
||||
Name: "Callbacks",
|
||||
Description: &description,
|
||||
},
|
||||
ResourceTypes: &resourceTypes,
|
||||
}
|
||||
if err := pdb.Create(ctx, policyDescription); err != nil && !errors.Is(err, merrors.ErrDataConflict) {
|
||||
if logger != nil {
|
||||
logger.Warn("Failed to create built-in callbacks policy", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return pdb.GetBuiltInPolicy(ctx, mservice.Callbacks, &existing)
|
||||
}
|
||||
|
||||
var _ callbacks.DB = (*CallbacksDB)(nil)
|
||||
|
||||
60
api/pkg/db/internal/mongo/callbacksdb/secretref.go
Normal file
60
api/pkg/db/internal/mongo/callbacksdb/secretref.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package callbacksdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/repository"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
func (db *CallbacksDB) GetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) (string, error) {
|
||||
if callbackRef.IsZero() {
|
||||
return "", merrors.InvalidArgument("callback reference is required", "callbackRef")
|
||||
}
|
||||
|
||||
// Enforce read permissions through the public callback object first.
|
||||
var callback model.Callback
|
||||
if err := db.Get(ctx, accountRef, callbackRef, &callback); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
internal := &callbackInternal{}
|
||||
if err := db.DBImp.Repository.Get(ctx, callbackRef, internal); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(internal.SecretRef), nil
|
||||
}
|
||||
|
||||
func (db *CallbacksDB) SetSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID, secretRef string) error {
|
||||
if callbackRef.IsZero() {
|
||||
return merrors.InvalidArgument("callback reference is required", "callbackRef")
|
||||
}
|
||||
value := strings.TrimSpace(secretRef)
|
||||
if value == "" {
|
||||
return merrors.InvalidArgument("secret reference is required", "secretRef")
|
||||
}
|
||||
|
||||
return db.Patch(
|
||||
ctx,
|
||||
accountRef,
|
||||
callbackRef,
|
||||
repository.Patch().Set(repository.Field("secretRef"), value),
|
||||
)
|
||||
}
|
||||
|
||||
func (db *CallbacksDB) ClearSigningSecretRef(ctx context.Context, accountRef, callbackRef bson.ObjectID) error {
|
||||
if callbackRef.IsZero() {
|
||||
return merrors.InvalidArgument("callback reference is required", "callbackRef")
|
||||
}
|
||||
|
||||
return db.Patch(
|
||||
ctx,
|
||||
accountRef,
|
||||
callbackRef,
|
||||
repository.Patch().Unset(repository.Field("secretRef")),
|
||||
)
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
mutil "github.com/tech/sendico/pkg/mutil/db"
|
||||
"github.com/tech/sendico/pkg/mutil/mzap"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -54,7 +55,6 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.Any("scope_filter", scopeFilter.BuildQuery()),
|
||||
)
|
||||
|
||||
// 1) Fast path for magic-link tokens: hash is deterministic and globally unique.
|
||||
@@ -69,7 +69,6 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.Any("magic_filter", magicFilter.BuildQuery()),
|
||||
)
|
||||
var direct model.VerificationToken
|
||||
err := db.DBImp.FindOne(ctx, magicFilter, &direct)
|
||||
@@ -152,7 +151,7 @@ func (db *verificationDB) Consume(
|
||||
db.Logger.Debug("Verification consume OTP candidate evaluated",
|
||||
zap.Int("candidate_index", i),
|
||||
zap.Int("candidate_total", len(tokens)),
|
||||
zap.String("candidate_id", t.ID.Hex()),
|
||||
mzap.ObjRef("candidate_ref", t.ID),
|
||||
zap.Bool("candidate_has_salt", t.Salt != nil),
|
||||
zap.Bool("candidate_used", t.UsedAt != nil),
|
||||
zap.Time("candidate_expires_at", t.ExpiresAt),
|
||||
@@ -181,7 +180,6 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.Any("active_filter", activeFilter.BuildQuery()),
|
||||
)
|
||||
|
||||
incremented, patchErr := db.DBImp.PatchMany(
|
||||
@@ -271,8 +269,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
zap.Any("consume_filter", consumeFilter.BuildQuery()),
|
||||
mzap.StorableRef(token),
|
||||
)
|
||||
|
||||
updated, err := db.DBImp.PatchMany(
|
||||
@@ -285,7 +282,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, err
|
||||
@@ -294,7 +291,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Int("updated_count", updated),
|
||||
)
|
||||
|
||||
@@ -322,7 +319,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Error(incrementErr),
|
||||
)
|
||||
} else {
|
||||
@@ -330,7 +327,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Int("updated_count", incremented),
|
||||
zap.String("transaction_note", "this update occurs inside transaction and may roll back depending on returned error"),
|
||||
)
|
||||
@@ -343,7 +340,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Error(err),
|
||||
)
|
||||
return nil, merrors.Internal("failed to re-check token state")
|
||||
@@ -353,7 +350,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
}, tokenStateFields(&fresh)...,
|
||||
)...,
|
||||
)
|
||||
@@ -363,7 +360,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
)
|
||||
return nil, verification.ErorrTokenAlreadyUsed()
|
||||
}
|
||||
@@ -372,7 +369,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Time("now_utc", now),
|
||||
zap.Time("token_expires_at", fresh.ExpiresAt),
|
||||
)
|
||||
@@ -383,7 +380,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
zap.Int("token_attempts", fresh.Attempts),
|
||||
zap.Int("token_max_retries", *fresh.MaxRetries),
|
||||
)
|
||||
@@ -394,7 +391,7 @@ func (db *verificationDB) Consume(
|
||||
zap.String("purpose", string(purpose)),
|
||||
zap.Bool("account_scoped", accountScoped),
|
||||
zap.String("account_ref", accountRefHex),
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
)
|
||||
return nil, verification.ErorrTokenNotFound()
|
||||
},
|
||||
@@ -457,8 +454,8 @@ func tokenIsDigitsOnly(value string) bool {
|
||||
|
||||
func tokenStateFields(token *model.VerificationToken) []zap.Field {
|
||||
fields := []zap.Field{
|
||||
zap.String("token_id", token.ID.Hex()),
|
||||
zap.String("token_account_ref", token.AccountRef.Hex()),
|
||||
mzap.StorableRef(token),
|
||||
mzap.ObjRef("token_account_ref", token.AccountRef),
|
||||
zap.String("token_purpose", string(token.Purpose)),
|
||||
zap.Bool("token_has_target", strings.TrimSpace(token.Target) != ""),
|
||||
zap.Bool("token_has_idempotency_key", token.IdempotencyKey != nil),
|
||||
|
||||
@@ -29,14 +29,6 @@ func syntheticIdempotencyKey() string {
|
||||
return "auto:" + bson.NewObjectID().Hex()
|
||||
}
|
||||
|
||||
func verificationContextFilter(request *verification.Request) builder.Query {
|
||||
return repository.Query().And(
|
||||
repository.Filter("accountRef", request.AccountRef),
|
||||
repository.Filter("purpose", request.Purpose),
|
||||
repository.Filter("target", request.Target),
|
||||
)
|
||||
}
|
||||
|
||||
func activeContextFilter(request *verification.Request, now time.Time) builder.Query {
|
||||
return repository.Query().And(
|
||||
repository.Filter("accountRef", request.AccountRef),
|
||||
|
||||
@@ -28,7 +28,7 @@ require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc // indirect
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||
|
||||
@@ -6,8 +6,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc h1:1stW1OipdBj8Me+nj26SzT8yil7OYve0r3cWobzk1JQ=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260225065256-91dd007ecddc/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b h1:zoMPWbn27kscAQflTWzSHhm9fuex5SXSpyMlhCFPfxk=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260302101851-b43f15d4ea3b/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
||||
@@ -16,11 +16,14 @@ const (
|
||||
CallbackSigningModeHMACSHA256 CallbackSigningMode = "hmac_sha256"
|
||||
)
|
||||
|
||||
type CallbackBackoff struct {
|
||||
MinDelayMS int `bson:"min_ms" json:"minDelayMs"`
|
||||
MaxDelayMS int `bson:"max_ms" json:"maxDelayMs"`
|
||||
}
|
||||
|
||||
type CallbackRetryPolicy struct {
|
||||
MinDelayMS int `bson:"min_ms" json:"minDelayMs"`
|
||||
MaxDelayMS int `bson:"max_ms" json:"maxDelayMs"`
|
||||
Backoff CallbackBackoff `bson:"backoff" json:"backoff"`
|
||||
SigningMode CallbackSigningMode `bson:"signing_mode" json:"signingMode"`
|
||||
SecretRef string `bson:"secret_ref,omitempty" json:"secretRef,omitempty"`
|
||||
Headers map[string]string `bson:"headers,omitempty" json:"headers,omitempty"`
|
||||
MaxAttempts int `bson:"max_attempts" json:"maxAttempts"`
|
||||
RequestTimeoutMS int `bson:"request_timeout_ms" json:"requestTimeoutMs"`
|
||||
@@ -29,7 +32,6 @@ type CallbackRetryPolicy struct {
|
||||
type Callback struct {
|
||||
PermissionBound `bson:",inline" json:",inline"`
|
||||
Describable `bson:",inline" json:",inline"`
|
||||
ClientID string `bson:"client_id" json:"clientId"`
|
||||
Status CallbackStatus `bson:"status" json:"status"`
|
||||
URL string `bson:"url" json:"url"`
|
||||
EventTypes []string `bson:"event_types" json:"eventTypes"`
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
const (
|
||||
PaymentStatusUpdatedType = "payment.status.updated"
|
||||
@@ -9,21 +13,20 @@ const (
|
||||
type PaymentStatusUpdated struct {
|
||||
EventID string `json:"event_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
OccurredAt time.Time `json:"occurred_at,omitempty"`
|
||||
PublishedAt time.Time `json:"published_at,omitempty"`
|
||||
Data PaymentStatusUpdatedData `json:"data"`
|
||||
}
|
||||
|
||||
type PaymentStatusUpdatedData struct {
|
||||
OrganizationRef string `json:"organization_ref,omitempty"`
|
||||
PaymentRef string `json:"payment_ref,omitempty"`
|
||||
QuotationRef string `json:"quotation_ref,omitempty"`
|
||||
ClientPaymentRef string `json:"client_payment_ref,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
PreviousState string `json:"previous_state,omitempty"`
|
||||
Version uint64 `json:"version,omitempty"`
|
||||
IsTerminal bool `json:"is_terminal"`
|
||||
Event string `json:"event,omitempty"`
|
||||
OrganizationRef bson.ObjectID `json:"organization_ref,omitempty"`
|
||||
PaymentRef string `json:"payment_ref,omitempty"`
|
||||
QuotationRef string `json:"quotation_ref,omitempty"`
|
||||
ClientPaymentRef string `json:"client_payment_ref,omitempty"`
|
||||
IdempotencyKey string `json:"idempotency_key,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
PreviousState string `json:"previous_state,omitempty"`
|
||||
Version uint64 `json:"version,omitempty"`
|
||||
IsTerminal bool `json:"is_terminal"`
|
||||
Event string `json:"event,omitempty"`
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package model
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
)
|
||||
|
||||
type ClientRefreshToken struct {
|
||||
@@ -12,13 +14,14 @@ type ClientRefreshToken struct {
|
||||
}
|
||||
|
||||
type RefreshToken struct {
|
||||
AccountBoundBase `bson:",inline" json:",inline"`
|
||||
storable.Base `bson:",inline" json:",inline"`
|
||||
ClientRefreshToken `bson:",inline" json:",inline"`
|
||||
ExpiresAt time.Time `bson:"expiresAt"`
|
||||
IsRevoked bool `bson:"isRevoked"`
|
||||
LastUsedAt time.Time `bson:"lastUsedAt,omitempty"`
|
||||
UserAgent string `bson:"userAgent"`
|
||||
IPAddress string `bson:"ipAddress"`
|
||||
AccountRef *bson.ObjectID `bson:"accountRef,omitempty" json:"accountRef,omitempty"`
|
||||
ExpiresAt time.Time `bson:"expiresAt"`
|
||||
IsRevoked bool `bson:"isRevoked"`
|
||||
LastUsedAt time.Time `bson:"lastUsedAt,omitempty"`
|
||||
UserAgent string `bson:"userAgent"`
|
||||
IPAddress string `bson:"ipAddress"`
|
||||
}
|
||||
|
||||
func (*RefreshToken) Collection() string {
|
||||
|
||||
Reference in New Issue
Block a user