Show OAuth2 errors to end users (#25261)
Partially fix #23936 ![image](https://github.com/go-gitea/gitea/assets/2114189/8aa7f3ad-a5f0-42ce-a478-289a03bd08a3) ![image](https://github.com/go-gitea/gitea/assets/2114189/bb901e7d-485a-47a5-b68d-9ebe7013a6b2) ![image](https://github.com/go-gitea/gitea/assets/2114189/9a1ce0f3-f011-4baf-8e2f-cc6304bc9703)
This commit is contained in:
parent
39a15623f6
commit
73ae71824d
6 changed files with 48 additions and 10 deletions
|
@ -2901,7 +2901,7 @@ auths.sspi_default_language = Default user language
|
||||||
auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected.
|
auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected.
|
||||||
auths.tips = Tips
|
auths.tips = Tips
|
||||||
auths.tips.oauth2.general = OAuth2 Authentication
|
auths.tips.oauth2.general = OAuth2 Authentication
|
||||||
auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be: <host>/user/oauth2/<Authentication Name>/callback
|
auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be:
|
||||||
auths.tip.oauth2_provider = OAuth2 Provider
|
auths.tip.oauth2_provider = OAuth2 Provider
|
||||||
auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user/<your username>/oauth-consumers/new and add the permission 'Account' - 'Read'
|
auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user/<your username>/oauth-consumers/new and add the permission 'Account' - 'Read'
|
||||||
auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client"
|
auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
stdContext "context"
|
go_context "context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -12,6 +12,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
|
@ -39,6 +40,7 @@ import (
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/markbates/goth"
|
"github.com/markbates/goth"
|
||||||
"github.com/markbates/goth/gothic"
|
"github.com/markbates/goth/gothic"
|
||||||
|
go_oauth2 "golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -143,7 +145,7 @@ type AccessTokenResponse struct {
|
||||||
IDToken string `json:"id_token,omitempty"`
|
IDToken string `json:"id_token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAccessTokenResponse(ctx stdContext.Context, grant *auth.OAuth2Grant, serverKey, clientKey oauth2.JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) {
|
func newAccessTokenResponse(ctx go_context.Context, grant *auth.OAuth2Grant, serverKey, clientKey oauth2.JWTSigningKey) (*AccessTokenResponse, *AccessTokenError) {
|
||||||
if setting.OAuth2.InvalidateRefreshTokens {
|
if setting.OAuth2.InvalidateRefreshTokens {
|
||||||
if err := grant.IncreaseCounter(ctx); err != nil {
|
if err := grant.IncreaseCounter(ctx); err != nil {
|
||||||
return nil, &AccessTokenError{
|
return nil, &AccessTokenError{
|
||||||
|
@ -886,6 +888,17 @@ func SignInOAuth(ctx *context.Context) {
|
||||||
func SignInOAuthCallback(ctx *context.Context) {
|
func SignInOAuthCallback(ctx *context.Context) {
|
||||||
provider := ctx.Params(":provider")
|
provider := ctx.Params(":provider")
|
||||||
|
|
||||||
|
if ctx.Req.FormValue("error") != "" {
|
||||||
|
var errorKeyValues []string
|
||||||
|
for k, vv := range ctx.Req.Form {
|
||||||
|
for _, v := range vv {
|
||||||
|
errorKeyValues = append(errorKeyValues, fmt.Sprintf("%s = %s", html.EscapeString(k), html.EscapeString(v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(errorKeyValues)
|
||||||
|
ctx.Flash.Error(strings.Join(errorKeyValues, "<br>"), true)
|
||||||
|
}
|
||||||
|
|
||||||
// first look if the provider is still active
|
// first look if the provider is still active
|
||||||
authSource, err := auth.GetActiveOAuth2SourceByName(provider)
|
authSource, err := auth.GetActiveOAuth2SourceByName(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -894,7 +907,7 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if authSource == nil {
|
if authSource == nil {
|
||||||
ctx.ServerError("SignIn", errors.New("No valid provider found, check configured callback url in provider"))
|
ctx.ServerError("SignIn", errors.New("no valid provider found, check configured callback url in provider"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,6 +933,9 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login")
|
ctx.Redirect(setting.AppSubURL + "/user/login")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err, ok := err.(*go_oauth2.RetrieveError); ok {
|
||||||
|
ctx.Flash.Error("OAuth2 RetrieveError: "+err.Error(), true)
|
||||||
|
}
|
||||||
ctx.ServerError("UserSignIn", err)
|
ctx.ServerError("UserSignIn", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
<span>{{.Source.TypeName}}</span>
|
<span>{{.Source.TypeName}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="required inline field {{if .Err_Name}}error{{end}}">
|
<div class="required inline field {{if .Err_Name}}error{{end}}">
|
||||||
<label for="name">{{.locale.Tr "admin.auths.auth_name"}}</label>
|
<label for="auth_name">{{.locale.Tr "admin.auths.auth_name"}}</label>
|
||||||
<input id="name" name="name" value="{{.Source.Name}}" autofocus required>
|
<input id="auth_name" name="name" value="{{.Source.Name}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- LDAP and DLDAP -->
|
<!-- LDAP and DLDAP -->
|
||||||
|
@ -434,6 +434,17 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h4 class="ui top attached header">
|
||||||
|
{{.locale.Tr "admin.auths.tips"}}
|
||||||
|
</h4>
|
||||||
|
<div class="ui attached segment">
|
||||||
|
<h5>GMail Settings:</h5>
|
||||||
|
<p>Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true</p>
|
||||||
|
|
||||||
|
<h5 class="oauth2">{{.locale.Tr "admin.auths.tips.oauth2.general"}}:</h5>
|
||||||
|
<p class="oauth2">{{.locale.Tr "admin.auths.tips.oauth2.general.tip"}} <b id="oauth2-callback-url"></b></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui g-modal-confirm delete modal">
|
<div class="ui g-modal-confirm delete modal">
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="required inline field {{if .Err_Name}}error{{end}}">
|
<div class="required inline field {{if .Err_Name}}error{{end}}">
|
||||||
<label for="name">{{.locale.Tr "admin.auths.auth_name"}}</label>
|
<label for="auth_name">{{.locale.Tr "admin.auths.auth_name"}}</label>
|
||||||
<input id="name" name="name" value="{{.name}}" autofocus required>
|
<input id="auth_name" name="name" value="{{.name}}" autofocus required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- LDAP and DLDAP -->
|
<!-- LDAP and DLDAP -->
|
||||||
|
@ -85,8 +85,8 @@
|
||||||
<h5>GMail Settings:</h5>
|
<h5>GMail Settings:</h5>
|
||||||
<p>Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true</p>
|
<p>Host: smtp.gmail.com, Port: 587, Enable TLS Encryption: true</p>
|
||||||
|
|
||||||
<h5>{{.locale.Tr "admin.auths.tips.oauth2.general"}}:</h5>
|
<h5 class="oauth2">{{.locale.Tr "admin.auths.tips.oauth2.general"}}:</h5>
|
||||||
<p>{{.locale.Tr "admin.auths.tips.oauth2.general.tip"}}</p>
|
<p class="oauth2">{{.locale.Tr "admin.auths.tips.oauth2.general.tip"}} <b id="oauth2-callback-url"></b></p>
|
||||||
|
|
||||||
<h5 class="ui top attached header">{{.locale.Tr "admin.auths.tip.oauth2_provider"}}</h5>
|
<h5 class="ui top attached header">{{.locale.Tr "admin.auths.tip.oauth2_provider"}}</h5>
|
||||||
<div class="ui attached segment">
|
<div class="ui attached segment">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
|
{{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
|
||||||
* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, DefaultTheme, Str2html
|
* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, DefaultTheme, Str2html
|
||||||
* locale
|
* locale
|
||||||
|
* Flash
|
||||||
* ErrorMsg
|
* ErrorMsg
|
||||||
* SignedUser (optional)
|
* SignedUser (optional)
|
||||||
*/}}
|
*/}}
|
||||||
|
@ -28,6 +29,10 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div role="main" class="page-content status-page-500">
|
<div role="main" class="page-content status-page-500">
|
||||||
|
<div class="ui container" >
|
||||||
|
<style> .ui.message.flash-message { text-align: left; } </style>
|
||||||
|
{{template "base/alert" .}}
|
||||||
|
</div>
|
||||||
<p class="gt-mt-5 center"><img src="{{AssetUrlPrefix}}/img/500.png" alt="Internal Server Error"></p>
|
<p class="gt-mt-5 center"><img src="{{AssetUrlPrefix}}/img/500.png" alt="Internal Server Error"></p>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui container gt-my-5">
|
<div class="ui container gt-my-5">
|
||||||
|
|
|
@ -171,6 +171,12 @@ export function initAdminCommon() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($('.admin.authentication').length > 0) {
|
||||||
|
$('#auth_name').on('input', function () {
|
||||||
|
$('#oauth2-callback-url').text(`${window.location.origin}/user/oauth2/${encodeURIComponent($(this).val())}/callback`);
|
||||||
|
}).trigger('input');
|
||||||
|
}
|
||||||
|
|
||||||
// Notice
|
// Notice
|
||||||
if ($('.admin.notice')) {
|
if ($('.admin.notice')) {
|
||||||
const $detailModal = $('#detail-modal');
|
const $detailModal = $('#detail-modal');
|
||||||
|
|
Loading…
Reference in a new issue