mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-11 16:45:13 +00:00
feat(login): add OTP (email and sms) (#6353)
* feat: login with otp * fix(i18n): japanese translation * add missing files * fix provider change * add event types translations to en * add tests * resourceOwner * remove unused handler * fix: secret generators and add comments * add setup step * rename * linting * fix setup * improve otp handling * fix autocomplete * translations for login and notifications * translations for event types * changes from review * check selected mfa type
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
{{template "main-top" .}}
|
||||
|
||||
<div class="lgn-head">
|
||||
<h1>{{t "InitMFAOTPSMS.Title"}}</h1>
|
||||
|
||||
{{ template "user-profile" . }}
|
||||
|
||||
{{if .Edit}}
|
||||
<p>{{t "InitMFAOTPSMS.DescriptionPhone"}}</p>
|
||||
{{else}}
|
||||
<p>{{t "InitMFAOTPSMS.DescriptionCode"}}</p>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<form action="{{ mfaInitSMSVerifyUrl }}" method="POST">
|
||||
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="mfaType" value="{{ .MFAType }}" />
|
||||
<input type="hidden" name="phone" value="{{ .Phone }}" />
|
||||
|
||||
<div class="fields">
|
||||
{{if .Edit}}
|
||||
<div class="field">
|
||||
<label class="lgn-label" for="newPhone">{{t "InitMFAOTPSMS.PhoneLabel"}}</label>
|
||||
<input class="lgn-input" type="tel" id="newPhone" name="newPhone" autocomplete="off" value="{{.Phone}}" autofocus required>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="field lgn-actions">
|
||||
<p>{{.Phone}}</p>
|
||||
<span class="fill-space"></span>
|
||||
<button type="button" id="edit" name="edit" value="true" class="lgn-stroked-button" formnovalidate>{{t "InitMFAOTPSMS.EditButtonText"}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if and .Phone (not .Edit) }}
|
||||
<div class="field">
|
||||
<label class="lgn-label" for="code">{{t "InitMFAOTPSMS.CodeLabel"}}</label>
|
||||
<input class="lgn-input" type="text" id="code" name="code" autocomplete="one-time-code" autofocus required>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{ template "error-message" .}}
|
||||
|
||||
<div class="lgn-actions lgn-reverse-order">
|
||||
<!-- position element in header -->
|
||||
<a class="lgn-icon-button lgn-left-action" href="{{ mfaPromptChangeUrl .AuthReqID .MFAType }}">
|
||||
<i class="lgn-icon-arrow-left-solid"></i>
|
||||
</a>
|
||||
<button class="lgn-raised-button lgn-primary" id="submit-button" type="submit">{{t "InitMFAOTPSMS.NextButtonText"}}</button>
|
||||
{{if and .Phone (not .Edit) }}
|
||||
<span class="fill-space"></span>
|
||||
<button type="submit" name="resend" value="true" class="lgn-stroked-button" formnovalidate>{{t "InitMFAOTPSMS.ResendButtonText"}}</button>
|
||||
{{end}}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script src="{{ resourceUrl "scripts/edit.js" }}"></script>
|
||||
<script src="{{ resourceUrl "scripts/form_submit.js" }}"></script>
|
||||
<script src="{{ resourceUrl "scripts/default_form_validation.js" }}"></script>
|
||||
|
||||
{{template "main-bottom" .}}
|
||||
@@ -34,6 +34,16 @@
|
||||
<img width="100px" height="100px" alt="OTP" src="{{ resourceUrl
|
||||
"images/mfa/mfa-u2f.svg" }}" />
|
||||
</div>
|
||||
{{ end }} {{ if eq $provider 3 }}
|
||||
<div class="mfa-img">
|
||||
<img width="100px" height="100px" alt="OTP SMS" src="{{ resourceUrl
|
||||
"images/mfa/mfa-u2f.svg" }}" /> // TODO: image
|
||||
</div>
|
||||
{{ end }}{{ if eq $provider 4 }}
|
||||
<div class="mfa-img">
|
||||
<img width="100px" height="100px" alt="OTP Email" src="{{ resourceUrl
|
||||
"images/mfa/mfa-u2f.svg" }}" /> // TODO: image
|
||||
</div>
|
||||
{{ end }}
|
||||
<span>{{ $providerName }} </span>
|
||||
</label>
|
||||
|
||||
@@ -1,34 +1,37 @@
|
||||
{{template "main-top" .}}
|
||||
|
||||
<div class="lgn-head">
|
||||
<h1>{{t "VerifyMFAOTP.Title"}}</h1>
|
||||
<h1>{{t "VerifyOTP.Title"}}</h1>
|
||||
|
||||
{{ template "user-profile" . }}
|
||||
|
||||
<p>{{t "VerifyMFAOTP.Description"}}</p>
|
||||
<p>{{t "VerifyOTP.Description"}}</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ mfaVerifyUrl }}" method="POST">
|
||||
<form action="{{ mfaOTPVerifyUrl }}" method="POST">
|
||||
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="mfaType" value="{{ .SelectedMFAProvider }}" />
|
||||
<input type="hidden" name="selectedProvider" value="{{ .SelectedProvider }}" />
|
||||
|
||||
<div class="fields">
|
||||
<label class="lgn-label" for="code">{{t "VerifyMFAOTP.CodeLabel"}}</label>
|
||||
<input class="lgn-input" type="text" id="code" name="code" autocomplete="off" autofocus required>
|
||||
<label class="lgn-label" for="code">{{t "VerifyOTP.CodeLabel"}}</label>
|
||||
<input class="lgn-input" type="text" id="code" name="code" autocomplete="one-time-code" autofocus required>
|
||||
</div>
|
||||
|
||||
{{ template "error-message" .}}
|
||||
|
||||
<div class="lgn-actions">
|
||||
<div class="lgn-actions lgn-reverse-order">
|
||||
<!-- position element in header -->
|
||||
<a class="lgn-icon-button lgn-left-action" href="{{ loginUrl }}">
|
||||
<i class="lgn-icon-arrow-left-solid"></i>
|
||||
</a>
|
||||
<button class="lgn-raised-button lgn-primary" id="submit-button" type="submit">{{t "VerifyOTP.NextButtonText"}}</button>
|
||||
|
||||
<span class="fill-space"></span>
|
||||
<button class="lgn-raised-button lgn-primary" id="submit-button" type="submit">{{t "VerifyMFAOTP.NextButtonText"}}</button>
|
||||
|
||||
<button type="submit" name="resend" value="true" class="lgn-stroked-button" formnovalidate>{{t "VerifyOTP.ResendButtonText"}}</button>
|
||||
</div>
|
||||
|
||||
{{ if .MFAProviders }}
|
||||
@@ -45,4 +48,4 @@
|
||||
|
||||
<script src="{{ resourceUrl "scripts/form_submit.js" }}"></script>
|
||||
<script src="{{ resourceUrl "scripts/default_form_validation.js" }}"></script>
|
||||
{{template "main-bottom" .}}
|
||||
{{template "main-bottom" .}}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
{{template "main-top" .}}
|
||||
|
||||
<div class="lgn-head">
|
||||
<h1>{{t "VerifyMFAOTP.Title"}}</h1>
|
||||
|
||||
{{ template "user-profile" . }}
|
||||
|
||||
<p>{{t "VerifyMFAOTP.Description"}}</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ mfaVerifyUrl }}" method="POST">
|
||||
|
||||
{{ .CSRF }}
|
||||
|
||||
<input type="hidden" name="authRequestID" value="{{ .AuthReqID }}" />
|
||||
<input type="hidden" name="mfaType" value="{{ .SelectedMFAProvider }}" />
|
||||
|
||||
<div class="fields">
|
||||
<label class="lgn-label" for="code">{{t "VerifyMFAOTP.CodeLabel"}}</label>
|
||||
<input class="lgn-input" type="text" id="code" name="code" autocomplete="off" autofocus required>
|
||||
</div>
|
||||
|
||||
{{ template "error-message" .}}
|
||||
|
||||
<div class="lgn-actions">
|
||||
<!-- position element in header -->
|
||||
<a class="lgn-icon-button lgn-left-action" href="{{ loginUrl }}">
|
||||
<i class="lgn-icon-arrow-left-solid"></i>
|
||||
</a>
|
||||
<span class="fill-space"></span>
|
||||
<button class="lgn-raised-button lgn-primary" id="submit-button" type="submit">{{t "VerifyMFAOTP.NextButtonText"}}</button>
|
||||
</div>
|
||||
|
||||
{{ if .MFAProviders }}
|
||||
<div class="lgn-mfa-other">
|
||||
<p>{{t "MFAProvider.ChooseOther"}}</p>
|
||||
{{ range $provider := .MFAProviders}}
|
||||
{{ $providerName := (t (printf "MFAProvider.Provider%v" $provider)) }}
|
||||
<button class="lgn-stroked-button" type="submit" name="provider" value="{{$provider}}"
|
||||
formnovalidate>{{$providerName}}</button>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</form>
|
||||
|
||||
<script src="{{ resourceUrl "scripts/form_submit.js" }}"></script>
|
||||
<script src="{{ resourceUrl "scripts/default_form_validation.js" }}"></script>
|
||||
{{template "main-bottom" .}}
|
||||
Reference in New Issue
Block a user