mirror of
https://github.com/writefreely/writefreely
synced 2024-12-01 04:49:09 +00:00
Merge pull request #317 from pascoual/feature/generic-oauth
Login with generic oauth feature++
This commit is contained in:
commit
dfa14c9c92
11 changed files with 304 additions and 57 deletions
27
account.go
27
account.go
|
@ -86,6 +86,11 @@ func apiSignup(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
}
|
||||
|
||||
func signup(app *App, w http.ResponseWriter, r *http.Request) (*AuthUser, error) {
|
||||
if app.cfg.App.DisablePasswordAuth {
|
||||
err := ErrDisabledPasswordAuth
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqJSON := IsJSON(r)
|
||||
|
||||
// Get params
|
||||
|
@ -307,6 +312,8 @@ func viewLogin(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
OauthWriteAs bool
|
||||
OauthGitlab bool
|
||||
GitlabDisplayName string
|
||||
OauthGeneric bool
|
||||
OauthGenericDisplayName string
|
||||
OauthGitea bool
|
||||
GiteaDisplayName string
|
||||
}{
|
||||
|
@ -318,6 +325,8 @@ func viewLogin(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
app.Config().SlackOauth.ClientID != "",
|
||||
app.Config().WriteAsOauth.ClientID != "",
|
||||
app.Config().GitlabOauth.ClientID != "",
|
||||
config.OrDefaultString(app.Config().GenericOauth.DisplayName, genericOauthDisplayName),
|
||||
app.Config().GenericOauth.ClientID != "",
|
||||
config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName),
|
||||
app.Config().GiteaOauth.ClientID != "",
|
||||
config.OrDefaultString(app.Config().GiteaOauth.DisplayName, giteaDisplayName),
|
||||
|
@ -395,6 +404,11 @@ func login(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
var err error
|
||||
var signin userCredentials
|
||||
|
||||
if app.cfg.App.DisablePasswordAuth {
|
||||
err := ErrDisabledPasswordAuth
|
||||
return err
|
||||
}
|
||||
|
||||
// Log in with one-time token if one is given
|
||||
if oneTimeToken != "" {
|
||||
log.Info("Login: Logging user in via token.")
|
||||
|
@ -1049,6 +1063,7 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
enableOauthSlack := app.Config().SlackOauth.ClientID != ""
|
||||
enableOauthWriteAs := app.Config().WriteAsOauth.ClientID != ""
|
||||
enableOauthGitLab := app.Config().GitlabOauth.ClientID != ""
|
||||
enableOauthGeneric := app.Config().GenericOauth.ClientID != ""
|
||||
enableOauthGitea := app.Config().GiteaOauth.ClientID != ""
|
||||
|
||||
oauthAccounts, err := app.db.GetOauthAccounts(r.Context(), u.ID)
|
||||
|
@ -1056,7 +1071,7 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
log.Error("Unable to get oauth accounts for settings: %s", err)
|
||||
return impart.HTTPError{http.StatusInternalServerError, "Unable to retrieve user data. The humans have been alerted."}
|
||||
}
|
||||
for _, oauthAccount := range oauthAccounts {
|
||||
for idx, oauthAccount := range oauthAccounts {
|
||||
switch oauthAccount.Provider {
|
||||
case "slack":
|
||||
enableOauthSlack = false
|
||||
|
@ -1064,12 +1079,16 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
enableOauthWriteAs = false
|
||||
case "gitlab":
|
||||
enableOauthGitLab = false
|
||||
case "generic":
|
||||
oauthAccounts[idx].DisplayName = app.Config().GenericOauth.DisplayName
|
||||
oauthAccounts[idx].AllowDisconnect = app.Config().GenericOauth.AllowDisconnect
|
||||
enableOauthGeneric = false
|
||||
case "gitea":
|
||||
enableOauthGitea = false
|
||||
}
|
||||
}
|
||||
|
||||
displayOauthSection := enableOauthSlack || enableOauthWriteAs || enableOauthGitLab || enableOauthGitea || len(oauthAccounts) > 0
|
||||
displayOauthSection := enableOauthSlack || enableOauthWriteAs || enableOauthGitLab || enableOauthGeneric || enableOauthGitea || len(oauthAccounts) > 0
|
||||
|
||||
obj := struct {
|
||||
*UserPage
|
||||
|
@ -1083,6 +1102,8 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
OauthWriteAs bool
|
||||
OauthGitLab bool
|
||||
GitLabDisplayName string
|
||||
OauthGeneric bool
|
||||
OauthGenericDisplayName string
|
||||
OauthGitea bool
|
||||
GiteaDisplayName string
|
||||
}{
|
||||
|
@ -1097,6 +1118,8 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
|
|||
OauthWriteAs: enableOauthWriteAs,
|
||||
OauthGitLab: enableOauthGitLab,
|
||||
GitLabDisplayName: config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName),
|
||||
OauthGeneric: enableOauthGeneric,
|
||||
OauthGenericDisplayName: config.OrDefaultString(app.Config().GenericOauth.DisplayName, genericOauthDisplayName),
|
||||
OauthGitea: enableOauthGitea,
|
||||
GiteaDisplayName: config.OrDefaultString(app.Config().GiteaOauth.DisplayName, giteaDisplayName),
|
||||
}
|
||||
|
|
13
app.go
13
app.go
|
@ -243,9 +243,22 @@ func handleViewLanding(app *App, w http.ResponseWriter, r *http.Request) error {
|
|||
Content template.HTML
|
||||
|
||||
ForcedLanding bool
|
||||
|
||||
OauthSlack bool
|
||||
OauthWriteAs bool
|
||||
OauthGitlab bool
|
||||
OauthGeneric bool
|
||||
OauthGenericDisplayName string
|
||||
GitlabDisplayName string
|
||||
}{
|
||||
StaticPage: pageForReq(app, r),
|
||||
ForcedLanding: forceLanding,
|
||||
OauthSlack: app.Config().SlackOauth.ClientID != "",
|
||||
OauthWriteAs: app.Config().WriteAsOauth.ClientID != "",
|
||||
OauthGitlab: app.Config().GitlabOauth.ClientID != "",
|
||||
OauthGeneric: app.Config().GenericOauth.ClientID != "",
|
||||
OauthGenericDisplayName: config.OrDefaultString(app.Config().GenericOauth.DisplayName, genericOauthDisplayName),
|
||||
GitlabDisplayName: config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName),
|
||||
}
|
||||
|
||||
banner, err := getLandingBanner(app)
|
||||
|
|
|
@ -89,6 +89,18 @@ type (
|
|||
CallbackProxyAPI string `ini:"callback_proxy_api"`
|
||||
}
|
||||
|
||||
GenericOauthCfg struct {
|
||||
ClientID string `ini:"client_id"`
|
||||
ClientSecret string `ini:"client_secret"`
|
||||
Host string `ini:"host"`
|
||||
DisplayName string `ini:"display_name"`
|
||||
CallbackProxy string `ini:"callback_proxy"`
|
||||
CallbackProxyAPI string `ini:"callback_proxy_api"`
|
||||
TokenEndpoint string `ini:"token_endpoint"`
|
||||
InspectEndpoint string `ini:"inspect_endpoint"`
|
||||
AuthEndpoint string `ini:"auth_endpoint"`
|
||||
AllowDisconnect bool `ini:"allow_disconnect"`
|
||||
}
|
||||
GiteaOauthCfg struct {
|
||||
ClientID string `ini:"client_id"`
|
||||
ClientSecret string `ini:"client_secret"`
|
||||
|
@ -140,6 +152,9 @@ type (
|
|||
|
||||
// Check for Updates
|
||||
UpdateChecks bool `ini:"update_checks"`
|
||||
|
||||
// Disable password authentication if use only Oauth
|
||||
DisablePasswordAuth bool `ini:"disable_password_auth"`
|
||||
}
|
||||
|
||||
// Config holds the complete configuration for running a writefreely instance
|
||||
|
@ -150,6 +165,7 @@ type (
|
|||
SlackOauth SlackOauthCfg `ini:"oauth.slack"`
|
||||
WriteAsOauth WriteAsOauthCfg `ini:"oauth.writeas"`
|
||||
GitlabOauth GitlabOauthCfg `ini:"oauth.gitlab"`
|
||||
GenericOauth GenericOauthCfg `ini:"oauth.generic"`
|
||||
GiteaOauth GiteaOauthCfg `ini:"oauth.gitea"`
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2630,6 +2630,8 @@ type oauthAccountInfo struct {
|
|||
Provider string
|
||||
ClientID string
|
||||
RemoteUserID string
|
||||
DisplayName string
|
||||
AllowDisconnect bool
|
||||
}
|
||||
|
||||
func (db *datastore) GetOauthAccounts(ctx context.Context, userID int64) ([]oauthAccountInfo, error) {
|
||||
|
|
|
@ -52,6 +52,8 @@ var (
|
|||
ErrUserNotFoundEmail = impart.HTTPError{http.StatusNotFound, "Please enter your username instead of your email address."}
|
||||
|
||||
ErrUserSilenced = impart.HTTPError{http.StatusForbidden, "Account is silenced."}
|
||||
|
||||
ErrDisabledPasswordAuth = impart.HTTPError{http.StatusForbidden, "Password authentication is disabled."}
|
||||
)
|
||||
|
||||
// Post operation errors
|
||||
|
|
27
oauth.go
27
oauth.go
|
@ -235,6 +235,33 @@ func configureGitlabOauth(parentHandler *Handler, r *mux.Router, app *App) {
|
|||
}
|
||||
}
|
||||
|
||||
func configureGenericOauth(parentHandler *Handler, r *mux.Router, app *App) {
|
||||
if app.Config().GenericOauth.ClientID != "" {
|
||||
callbackLocation := app.Config().App.Host + "/oauth/callback/generic"
|
||||
|
||||
var callbackProxy *callbackProxyClient = nil
|
||||
if app.Config().GenericOauth.CallbackProxy != "" {
|
||||
callbackProxy = &callbackProxyClient{
|
||||
server: app.Config().GenericOauth.CallbackProxyAPI,
|
||||
callbackLocation: app.Config().App.Host + "/oauth/callback/generic",
|
||||
httpClient: config.DefaultHTTPClient(),
|
||||
}
|
||||
callbackLocation = app.Config().GenericOauth.CallbackProxy
|
||||
}
|
||||
|
||||
oauthClient := genericOauthClient{
|
||||
ClientID: app.Config().GenericOauth.ClientID,
|
||||
ClientSecret: app.Config().GenericOauth.ClientSecret,
|
||||
ExchangeLocation: app.Config().GenericOauth.Host + app.Config().GenericOauth.TokenEndpoint,
|
||||
InspectLocation: app.Config().GenericOauth.Host + app.Config().GenericOauth.InspectEndpoint,
|
||||
AuthLocation: app.Config().GenericOauth.Host + app.Config().GenericOauth.AuthEndpoint,
|
||||
HttpClient: config.DefaultHTTPClient(),
|
||||
CallbackLocation: callbackLocation,
|
||||
}
|
||||
configureOauthRoutes(parentHandler, r, app, oauthClient, callbackProxy)
|
||||
}
|
||||
}
|
||||
|
||||
func configureGiteaOauth(parentHandler *Handler, r *mux.Router, app *App) {
|
||||
if app.Config().GiteaOauth.ClientID != "" {
|
||||
callbackLocation := app.Config().App.Host + "/oauth/callback/gitea"
|
||||
|
|
114
oauth_generic.go
Normal file
114
oauth_generic.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package writefreely
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type genericOauthClient struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
AuthLocation string
|
||||
ExchangeLocation string
|
||||
InspectLocation string
|
||||
CallbackLocation string
|
||||
HttpClient HttpClient
|
||||
}
|
||||
|
||||
var _ oauthClient = genericOauthClient{}
|
||||
|
||||
const (
|
||||
genericOauthDisplayName = "OAuth"
|
||||
)
|
||||
|
||||
func (c genericOauthClient) GetProvider() string {
|
||||
return "generic"
|
||||
}
|
||||
|
||||
func (c genericOauthClient) GetClientID() string {
|
||||
return c.ClientID
|
||||
}
|
||||
|
||||
func (c genericOauthClient) GetCallbackLocation() string {
|
||||
return c.CallbackLocation
|
||||
}
|
||||
|
||||
func (c genericOauthClient) buildLoginURL(state string) (string, error) {
|
||||
u, err := url.Parse(c.AuthLocation)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set("client_id", c.ClientID)
|
||||
q.Set("redirect_uri", c.CallbackLocation)
|
||||
q.Set("response_type", "code")
|
||||
q.Set("state", state)
|
||||
q.Set("scope", "read_user")
|
||||
u.RawQuery = q.Encode()
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (c genericOauthClient) exchangeOauthCode(ctx context.Context, code string) (*TokenResponse, error) {
|
||||
form := url.Values{}
|
||||
form.Add("grant_type", "authorization_code")
|
||||
form.Add("redirect_uri", c.CallbackLocation)
|
||||
form.Add("scope", "read_user")
|
||||
form.Add("code", code)
|
||||
req, err := http.NewRequest("POST", c.ExchangeLocation, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.WithContext(ctx)
|
||||
req.Header.Set("User-Agent", "writefreely")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth(c.ClientID, c.ClientSecret)
|
||||
|
||||
resp, err := c.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("unable to exchange code for access token")
|
||||
}
|
||||
|
||||
var tokenResponse TokenResponse
|
||||
if err := limitedJsonUnmarshal(resp.Body, tokenRequestMaxLen, &tokenResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenResponse.Error != "" {
|
||||
return nil, errors.New(tokenResponse.Error)
|
||||
}
|
||||
return &tokenResponse, nil
|
||||
}
|
||||
|
||||
func (c genericOauthClient) inspectOauthAccessToken(ctx context.Context, accessToken string) (*InspectResponse, error) {
|
||||
req, err := http.NewRequest("GET", c.InspectLocation, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.WithContext(ctx)
|
||||
req.Header.Set("User-Agent", "writefreely")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||
|
||||
resp, err := c.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("unable to inspect access token")
|
||||
}
|
||||
|
||||
var inspectResponse InspectResponse
|
||||
if err := limitedJsonUnmarshal(resp.Body, infoRequestMaxLen, &inspectResponse); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inspectResponse.Error != "" {
|
||||
return nil, errors.New(inspectResponse.Error)
|
||||
}
|
||||
return &inspectResponse, nil
|
||||
}
|
|
@ -60,6 +60,11 @@ form dd {
|
|||
margin-top: 0;
|
||||
max-width: 8em;
|
||||
}
|
||||
#generic-oauth-login {
|
||||
box-sizing: border-box;
|
||||
font-size: 17px;
|
||||
white-space:nowrap;
|
||||
}
|
||||
</style>
|
||||
{{end}}
|
||||
{{define "content"}}
|
||||
|
@ -73,6 +78,21 @@ form dd {
|
|||
|
||||
<div{{if not .OpenRegistration}} style="padding: 2em 0;"{{end}}>
|
||||
{{ if .OpenRegistration }}
|
||||
{{ if or .OauthSlack .OauthWriteAs .OauthGitlab .OauthGeneric }}
|
||||
{{ if .OauthSlack }}
|
||||
<div class="row content-container signinbtns signinoauthbtns"><a class="loginbtn" href="/oauth/slack"><img alt="Sign in with Slack" height="40" width="172" src="/img/sign_in_with_slack.png" srcset="/img/sign_in_with_slack.png 1x, /img/sign_in_with_slack@2x.png 2x" /></a></div>
|
||||
{{ end }}
|
||||
{{ if .OauthWriteAs }}
|
||||
<div class="row content-container signinbtns signinoauthbtns"><a class="btn cta loginbtn" id="writeas-login" href="/oauth/write.as">Sign in with <strong>Write.as</strong></a></div>
|
||||
{{ end }}
|
||||
{{ if .OauthGitlab }}
|
||||
<div class="row content-container signinbtns signinoauthbtns"><a class="btn cta loginbtn" id="gitlab-login" href="/oauth/gitlab">Sign in with <strong>{{.GitlabDisplayName}}</strong></a></div>
|
||||
{{ end }}
|
||||
{{ if .OauthGeneric }}
|
||||
<div class="row content-container signinbtns signinoauthbtns"><a class="btn cta loginbtn" id="generic-oauth-login" href="/oauth/generic">Sign in with <strong>{{ .OauthGenericDisplayName }}</strong></a></div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{if not .DisablePasswordAuth}}
|
||||
{{if .Flashes}}<ul class="errors">
|
||||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
|
||||
</ul>{{end}}
|
||||
|
@ -101,6 +121,7 @@ form dd {
|
|||
</dl>
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
{{ else }}
|
||||
<p style="font-size: 1.3em; margin: 1rem 0;">Registration is currently closed.</p>
|
||||
<p>You can always sign up on <a href="https://writefreely.org/instances">another instance</a>.</p>
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
<meta itemprop="description" content="Log in to {{.SiteName}}.">
|
||||
<style>
|
||||
input{margin-bottom:0.5em;}
|
||||
#generic-oauth-login {
|
||||
box-sizing: border-box;
|
||||
font-size: 17px;
|
||||
}
|
||||
</style>
|
||||
{{end}}
|
||||
{{define "content"}}
|
||||
|
@ -13,7 +17,7 @@ input{margin-bottom:0.5em;}
|
|||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
|
||||
</ul>{{end}}
|
||||
|
||||
{{ if or .OauthSlack .OauthWriteAs .OauthGitlab .OauthGitea }}
|
||||
{{ if or .OauthSlack .OauthWriteAs .OauthGitlab .OauthGeneric .OauthGitea }}
|
||||
<div class="row content-container signinbtns">
|
||||
{{ if .OauthSlack }}
|
||||
<a class="loginbtn" href="/oauth/slack"><img alt="Sign in with Slack" height="40" width="172" src="/img/sign_in_with_slack.png" srcset="/img/sign_in_with_slack.png 1x, /img/sign_in_with_slack@2x.png 2x" /></a>
|
||||
|
@ -24,17 +28,23 @@ input{margin-bottom:0.5em;}
|
|||
{{ if .OauthGitlab }}
|
||||
<a class="btn cta loginbtn" id="gitlab-login" href="/oauth/gitlab">Sign in with <strong>{{.GitlabDisplayName}}</strong></a>
|
||||
{{ end }}
|
||||
{{ if .OauthGeneric }}
|
||||
<a class="btn cta loginbtn" id="generic-oauth-login" href="/oauth/generic">Sign in with <strong>{{ .OauthGenericDisplayName }}</strong></a>
|
||||
{{ end }}
|
||||
{{ if .OauthGitea }}
|
||||
<a class="btn cta loginbtn" id="gitea-login" href="/oauth/gitea">Sign in with <strong>{{.GiteaDisplayName}}</strong></a>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{if not .DisablePasswordAuth}}
|
||||
<div class="or">
|
||||
<p>or</p>
|
||||
<hr class="short" />
|
||||
</div>
|
||||
{{end}}
|
||||
{{ end }}
|
||||
|
||||
{{if not .DisablePasswordAuth}}
|
||||
<form action="/auth/login" method="post" style="text-align: center;margin-top:1em;" onsubmit="disableSubmit()">
|
||||
<input type="text" name="alias" placeholder="Username" value="{{.LoginUsername}}" {{if not .LoginUsername}}autofocus{{end}} /><br />
|
||||
<input type="password" name="pass" placeholder="Password" {{if .LoginUsername}}autofocus{{end}} /><br />
|
||||
|
@ -44,11 +54,12 @@ input{margin-bottom:0.5em;}
|
|||
|
||||
{{if and (not .SingleUser) .OpenRegistration}}<p style="text-align:center;font-size:0.9em;margin:3em auto;max-width:26em;">{{if .Message}}{{.Message}}{{else}}<em>No account yet?</em> <a href="{{.SignupPath}}">Sign up</a> to start a blog.{{end}}</p>{{end}}
|
||||
|
||||
<script type="text/javascript">
|
||||
function disableSubmit() {
|
||||
<script type="text/javascript">
|
||||
function disableSubmit() {
|
||||
var $btn = document.getElementById("btn-login");
|
||||
$btn.value = "Logging in...";
|
||||
$btn.disabled = true;
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -76,6 +76,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
|||
configureSlackOauth(handler, write, apper.App())
|
||||
configureWriteAsOauth(handler, write, apper.App())
|
||||
configureGitlabOauth(handler, write, apper.App())
|
||||
configureGenericOauth(handler, write, apper.App())
|
||||
configureGiteaOauth(handler, write, apper.App())
|
||||
|
||||
// Set up dyamic page handlers
|
||||
|
|
|
@ -41,6 +41,7 @@ h3 { font-weight: normal; }
|
|||
</form>
|
||||
{{ end }}
|
||||
|
||||
{{if not .DisablePasswordAuth}}
|
||||
<form method="post" action="/api/me/self" autocomplete="false">
|
||||
<input type="hidden" name="logout" value="{{.IsLogOut}}" />
|
||||
<div class="option">
|
||||
|
@ -72,6 +73,7 @@ h3 { font-weight: normal; }
|
|||
<input type="submit" value="Save changes" tabindex="4" />
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
|
||||
{{ if .OauthSection }}
|
||||
<hr />
|
||||
|
@ -86,14 +88,22 @@ h3 { font-weight: normal; }
|
|||
<input type="hidden" name="client_id" value="{{ $oauth_account.ClientID }}" />
|
||||
<input type="hidden" name="remote_user_id" value="{{ $oauth_account.RemoteUserID }}" />
|
||||
<div class="section oauth-provider">
|
||||
{{ if $oauth_account.DisplayName}}
|
||||
{{ if $oauth_account.AllowDisconnect}}
|
||||
<input type="submit" value="Remove {{.DisplayName}}" />
|
||||
{{else}}
|
||||
<a class="btn cta"><strong>{{.DisplayName}}</strong></a>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<img src="/img/mark/{{$oauth_account.Provider}}.png" alt="{{ $oauth_account.Provider | title }}" />
|
||||
<input type="submit" value="Remove {{ $oauth_account.Provider | title }}" />
|
||||
{{end}}
|
||||
</div>
|
||||
</form>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if or .OauthSlack .OauthWriteAs .OauthGitLab .OauthGitea }}
|
||||
{{ if or .OauthSlack .OauthWriteAs .OauthGitLab .OauthGeneric .OauthGitea }}
|
||||
<div class="option">
|
||||
<h2>Link External Accounts</h2>
|
||||
<p>Connect additional accounts to enable logging in with those providers, instead of using your username and password.</p>
|
||||
|
@ -131,6 +141,13 @@ h3 { font-weight: normal; }
|
|||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if .OauthGeneric }}
|
||||
<div class="row">
|
||||
<div class="section oauth-provider">
|
||||
<p><a class="btn cta loginbtn" id="generic-oauth-login" href="/oauth/generic?attach=t">Link <strong>{{ .OauthGenericDisplayName }}</strong></a></p>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
Loading…
Reference in a new issue