Auth: Passwordless Login Option Using Magic Links (#95436)
* initial passwordless client * passwordless login page * Working basic e2e flow * Add todo comments * Improve the passwordless login flow * improved passwordless login, backend for passwordless signup * add expiration to emails * update email templates & render username & name fields on signup * improve email templates * change login page text while awaiting passwordless code * fix merge conflicts * use claims.TypeUser * add initial passwordless tests * better error messages * simplified error name * remove completed TODOs * linting & minor test improvements & rename passwordless routes * more linting fixes * move code generation to its own func, use locationService to get query params * fix ampersand in email templates & use passwordless api routes in LoginCtrl * txt emails more closely match html email copy * move passwordless auth behind experimental feature toggle * fix PasswordlessLogin property failing typecheck * make update-workspace * user correct placeholder * Update emails/templates/passwordless_verify_existing_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_existing_user.mjml Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.mjml Co-authored-by: Dan Cech <dcech@grafana.com> * use & in email templates * Update emails/templates/passwordless_verify_existing_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * remove IP address validation * struct for passwordless settings * revert go.work.sum changes * mock locationService.getSearch in failing test --------- Co-authored-by: Mihaly Gyongyosi <mgyongyosi@users.noreply.github.com> Co-authored-by: Dan Cech <dcech@grafana.com>
This commit is contained in:
co-authored by
Dan Cech
Mihaly Gyongyosi
parent
c865958292
commit
6abe99efd6
@@ -78,6 +78,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Get("/logout", hs.Logout)
|
||||
r.Post("/login", requestmeta.SetOwner(requestmeta.TeamAuth), quota(string(auth.QuotaTargetSrv)), routing.Wrap(hs.LoginPost))
|
||||
r.Get("/login/:name", quota(string(auth.QuotaTargetSrv)), hs.OAuthLogin)
|
||||
|
||||
r.Get("/login", hs.LoginView)
|
||||
r.Get("/invite/:code", hs.Index)
|
||||
|
||||
@@ -207,6 +208,11 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Post("/api/user/email/start-verify", reqSignedInNoAnonymous, routing.Wrap(hs.StartEmailVerificaton))
|
||||
}
|
||||
|
||||
if hs.Cfg.PasswordlessMagicLinkAuth.Enabled && hs.Features.IsEnabledGlobally(featuremgmt.FlagPasswordlessMagicLinkAuthentication) {
|
||||
r.Post("/api/login/passwordless/start", requestmeta.SetOwner(requestmeta.TeamAuth), quota(string(auth.QuotaTargetSrv)), hs.StartPasswordless)
|
||||
r.Post("/api/login/passwordless/authenticate", requestmeta.SetOwner(requestmeta.TeamAuth), quota(string(auth.QuotaTargetSrv)), routing.Wrap(hs.LoginPasswordless))
|
||||
}
|
||||
|
||||
// invited
|
||||
r.Get("/api/user/invite/:code", routing.Wrap(hs.GetInviteInfoByCode))
|
||||
r.Post("/api/user/invite/complete", routing.Wrap(hs.CompleteInvite))
|
||||
|
||||
@@ -33,6 +33,7 @@ type FrontendSettingsAuthDTO struct {
|
||||
|
||||
DisableLogin bool `json:"disableLogin"`
|
||||
BasicAuthStrongPasswordPolicy bool `json:"basicAuthStrongPasswordPolicy"`
|
||||
PasswordlessEnabled bool `json:"passwordlessEnabled"`
|
||||
}
|
||||
|
||||
type FrontendSettingsBuildInfoDTO struct {
|
||||
@@ -253,6 +254,7 @@ type FrontendSettingsDTO struct {
|
||||
TokenExpirationDayLimit int `json:"tokenExpirationDayLimit"`
|
||||
SharedWithMeFolderUID string `json:"sharedWithMeFolderUID"`
|
||||
RootFolderUID string `json:"rootFolderUID"`
|
||||
PasswordlessEnabled string `json:"passwordlessEnabled"`
|
||||
|
||||
GeomapDefaultBaseLayerConfig *map[string]any `json:"geomapDefaultBaseLayerConfig,omitempty"`
|
||||
GeomapDisableCustomBaseLayer bool `json:"geomapDisableCustomBaseLayer"`
|
||||
|
||||
@@ -360,6 +360,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
OktaSkipOrgRoleSync: parseSkipOrgRoleSyncEnabled(oauthProviders[social.OktaProviderName]),
|
||||
DisableLogin: hs.Cfg.DisableLogin,
|
||||
BasicAuthStrongPasswordPolicy: hs.Cfg.BasicAuthStrongPasswordPolicy,
|
||||
PasswordlessEnabled: hs.Cfg.PasswordlessMagicLinkAuth.Enabled && hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagPasswordlessMagicLinkAuthentication),
|
||||
}
|
||||
|
||||
if hs.pluginsCDNService != nil && hs.pluginsCDNService.IsEnabled() {
|
||||
|
||||
@@ -241,6 +241,26 @@ func (hs *HTTPServer) LoginPost(c *contextmodel.ReqContext) response.Response {
|
||||
return authn.HandleLoginResponse(c.Req, c.Resp, hs.Cfg, identity, hs.ValidateRedirectTo, hs.Features)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) LoginPasswordless(c *contextmodel.ReqContext) response.Response {
|
||||
identity, err := hs.authnService.Login(c.Req.Context(), authn.ClientPasswordless, &authn.Request{HTTPRequest: c.Req})
|
||||
if err != nil {
|
||||
tokenErr := &auth.CreateTokenErr{}
|
||||
if errors.As(err, &tokenErr) {
|
||||
return response.Error(tokenErr.StatusCode, tokenErr.ExternalErr, tokenErr.InternalErr)
|
||||
}
|
||||
return response.Err(err)
|
||||
}
|
||||
return authn.HandleLoginResponse(c.Req, c.Resp, hs.Cfg, identity, hs.ValidateRedirectTo, hs.Features)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) StartPasswordless(c *contextmodel.ReqContext) {
|
||||
redirect, err := hs.authnService.RedirectURL(c.Req.Context(), authn.ClientPasswordless, &authn.Request{HTTPRequest: c.Req})
|
||||
if err != nil {
|
||||
c.Redirect(hs.redirectURLWithErrorCookie(c, err))
|
||||
}
|
||||
c.JSON(http.StatusOK, redirect)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) loginUserWithUser(user *user.User, c *contextmodel.ReqContext) error {
|
||||
if user == nil {
|
||||
return errors.New("could not login user")
|
||||
|
||||
Reference in New Issue
Block a user