/
oob_otp_link.go
134 lines (112 loc) · 4.02 KB
/
oob_otp_link.go
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
package authflowv2
import (
htmltemplate "html/template"
"net/http"
"time"
handlerwebapp "github.com/authgear/authgear-server/pkg/auth/handler/webapp"
"github.com/authgear/authgear-server/pkg/auth/handler/webapp/viewmodels"
"github.com/authgear/authgear-server/pkg/auth/webapp"
"github.com/authgear/authgear-server/pkg/lib/authenticationflow/declarative"
"github.com/authgear/authgear-server/pkg/util/clock"
"github.com/authgear/authgear-server/pkg/util/httproute"
"github.com/authgear/authgear-server/pkg/util/template"
)
var TemplateWebAuthflowOOBOTPLinkHTML = template.RegisterHTML(
"web/authflowv2/oob_otp_link.html",
handlerwebapp.Components...,
)
func ConfigureV2AuthflowOOBOTPLinkRoute(route httproute.Route) httproute.Route {
return route.
WithMethods("OPTIONS", "POST", "GET").
WithPathPattern(AuthflowV2RouteOOBOTPLink)
}
type AuthflowOOBOTPLinkViewModel struct {
WebsocketURL htmltemplate.URL
StateToken string
StateQuery handlerwebapp.LoginLinkOTPPageQueryState
MaskedClaimValue string
ResendCooldown int
}
func NewAuthflowOOBOTPLinkViewModel(s *webapp.Session, screen *webapp.AuthflowScreenWithFlowResponse, now time.Time) AuthflowOOBOTPLinkViewModel {
var maskedClaimValue string
var resendCooldown int
var canCheck bool
var websocketURL string
switch data := screen.StateTokenFlowResponse.Action.Data.(type) {
case declarative.VerifyOOBOTPData:
maskedClaimValue = data.MaskedClaimValue
resendCooldown = int(data.CanResendAt.Sub(now).Seconds())
canCheck = data.CanCheck
websocketURL = data.WebsocketURL
default:
panic("authflowv2: unexpected action data")
}
if resendCooldown < 0 {
resendCooldown = 0
}
stateQuery := handlerwebapp.LoginLinkOTPPageQueryStateInitial
if canCheck {
stateQuery = handlerwebapp.LoginLinkOTPPageQueryStateMatched
}
return AuthflowOOBOTPLinkViewModel{
// nolint: gosec
WebsocketURL: htmltemplate.URL(websocketURL),
StateToken: screen.Screen.StateToken.StateToken,
StateQuery: stateQuery,
MaskedClaimValue: maskedClaimValue,
ResendCooldown: resendCooldown,
}
}
type AuthflowV2OOBOTPLinkHandler struct {
Controller *handlerwebapp.AuthflowController
BaseViewModel *viewmodels.BaseViewModeler
Renderer handlerwebapp.Renderer
Clock clock.Clock
}
func (h *AuthflowV2OOBOTPLinkHandler) GetData(w http.ResponseWriter, r *http.Request, s *webapp.Session, screen *webapp.AuthflowScreenWithFlowResponse) (map[string]interface{}, error) {
data := map[string]interface{}{}
baseViewModel := h.BaseViewModel.ViewModelForAuthFlow(r, w)
viewmodels.Embed(data, baseViewModel)
now := h.Clock.NowUTC()
screenViewModel := NewAuthflowOOBOTPLinkViewModel(s, screen, now)
viewmodels.Embed(data, screenViewModel)
branchViewModel := viewmodels.NewAuthflowBranchViewModel(screen)
viewmodels.Embed(data, branchViewModel)
return data, nil
}
func (h *AuthflowV2OOBOTPLinkHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var handlers handlerwebapp.AuthflowControllerHandlers
handlers.Get(func(s *webapp.Session, screen *webapp.AuthflowScreenWithFlowResponse) error {
data, err := h.GetData(w, r, s, screen)
if err != nil {
return err
}
h.Renderer.RenderHTML(w, r, TemplateWebAuthflowOOBOTPLinkHTML, data)
return nil
})
handlers.PostAction("resend", func(s *webapp.Session, screen *webapp.AuthflowScreenWithFlowResponse) error {
input := map[string]interface{}{
"resend": true,
}
result, err := h.Controller.UpdateWithInput(r, s, screen, input)
if err != nil {
return err
}
result.WriteResponse(w, r)
return nil
})
handlers.PostAction("check", func(s *webapp.Session, screen *webapp.AuthflowScreenWithFlowResponse) error {
requestDeviceToken := r.Form.Get("x_device_token") == "true"
input := map[string]interface{}{
"check": true,
"request_device_token": requestDeviceToken,
}
result, err := h.Controller.AdvanceWithInput(r, s, screen, input, nil)
if err != nil {
return err
}
result.WriteResponse(w, r)
return nil
})
h.Controller.HandleStep(w, r, &handlers)
}