-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathotp_model.go
More file actions
154 lines (127 loc) · 4.07 KB
/
otp_model.go
File metadata and controls
154 lines (127 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package core
import (
"context"
"errors"
"fmt"
"time"
"github.com/pocketbase/pocketbase/tools/hook"
"github.com/pocketbase/pocketbase/tools/types"
)
const CollectionNameOTPs = "_otps"
var (
_ Model = (*OTP)(nil)
_ PreValidator = (*OTP)(nil)
_ RecordProxy = (*OTP)(nil)
)
// OTP defines a Record proxy for working with the otps collection.
type OTP struct {
*Record
}
// NewOTP instantiates and returns a new blank *OTP model.
//
// Example usage:
//
// otp := core.NewOTP(app)
// otp.SetRecordRef(user.Id)
// otp.SetCollectionRef(user.Collection().Id)
// otp.SetPassword(security.RandomStringWithAlphabet(6, "1234567890"))
// app.Save(otp)
func NewOTP(app App) *OTP {
m := &OTP{}
c, err := app.FindCachedCollectionByNameOrId(CollectionNameOTPs)
if err != nil {
// this is just to make tests easier since otp is a system collection and it is expected to be always accessible
// (note: the loaded record is further checked on OTP.PreValidate())
c = NewBaseCollection("__invalid__")
}
m.Record = NewRecord(c)
return m
}
// PreValidate implements the [PreValidator] interface and checks
// whether the proxy is properly loaded.
func (m *OTP) PreValidate(ctx context.Context, app App) error {
if m.Record == nil || m.Record.Collection().Name != CollectionNameOTPs {
return errors.New("missing or invalid otp ProxyRecord")
}
return nil
}
// ProxyRecord returns the proxied Record model.
func (m *OTP) ProxyRecord() *Record {
return m.Record
}
// SetProxyRecord loads the specified record model into the current proxy.
func (m *OTP) SetProxyRecord(record *Record) {
m.Record = record
}
// CollectionRef returns the "collectionRef" field value.
func (m *OTP) CollectionRef() string {
return m.GetString("collectionRef")
}
// SetCollectionRef updates the "collectionRef" record field value.
func (m *OTP) SetCollectionRef(collectionId string) {
m.Set("collectionRef", collectionId)
}
// RecordRef returns the "recordRef" record field value.
func (m *OTP) RecordRef() string {
return m.GetString("recordRef")
}
// SetRecordRef updates the "recordRef" record field value.
func (m *OTP) SetRecordRef(recordId string) {
m.Set("recordRef", recordId)
}
// SentTo returns the "sentTo" record field value.
//
// It could be any string value (email, phone, message app id, etc.)
// and usually is used as part of the auth flow to update the verified
// user state in case for example the sentTo value matches with the user record email.
func (m *OTP) SentTo() string {
return m.GetString("sentTo")
}
// SetSentTo updates the "sentTo" record field value.
func (m *OTP) SetSentTo(val string) {
m.Set("sentTo", val)
}
// Created returns the "created" record field value.
func (m *OTP) Created() types.DateTime {
return m.GetDateTime("created")
}
// Updated returns the "updated" record field value.
func (m *OTP) Updated() types.DateTime {
return m.GetDateTime("updated")
}
// HasExpired checks if the otp is expired, aka. whether it has been
// more than maxElapsed time since its creation.
func (m *OTP) HasExpired(maxElapsed time.Duration) bool {
return time.Since(m.Created().Time()) > maxElapsed
}
func (app *BaseApp) registerOTPHooks() {
recordRefHooks[*OTP](app, CollectionNameOTPs, CollectionTypeAuth)
// run on every hour to cleanup expired otp sessions
app.Cron().Add("__pbOTPCleanup__", "0 * * * *", func() {
if err := app.DeleteExpiredOTPs(); err != nil {
app.Logger().Warn("Failed to delete expired OTP sessions", "error", err)
}
})
// delete all record OTPs on tokenKey change to minimize the risk of hijacking attacks
app.OnRecordUpdateExecute().Bind(&hook.Handler[*RecordEvent]{
Func: func(e *RecordEvent) error {
err := e.Next()
if err != nil || !e.Record.Collection().IsAuth() {
return err
}
if !e.Record.Original().IsNew() && e.Record.Original().TokenKey() != e.Record.TokenKey() {
err := e.App.DeleteAllOTPsByRecord(e.Record)
if err != nil {
return fmt.Errorf(
"[%s] failed to delete all previos OTPs for record %q: %w",
e.Record.Collection().Name,
e.Record.Id,
err,
)
}
}
return nil
},
Priority: 99,
})
}