mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-11-11 19:34:07 +01:00
- Add PKCE support with S256 challenge method for OAuth2 (fixes #852)
- Update UI for OAuth2 Code Challenge Method selection (closes #854 ) - Introduce OAuth2 configuration cache to optimize requests (fixes #852) - Fixes using DNS Challange for Custom ACME Server
This commit is contained in:
@@ -93,6 +93,7 @@ var (
|
|||||||
enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
|
enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
|
||||||
allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
|
||||||
enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")
|
||||||
|
oauth2ConfigurationCache = flag.Duration("oauth2cc", 60*time.Second, "Time in seconds to cache OAuth2 configuration, set to 0 to disable cache. Default: 60 seconds")
|
||||||
|
|
||||||
/* Logging Configuration Flags */
|
/* Logging Configuration Flags */
|
||||||
enableLog = flag.Bool("enablelog", true, "Enable system wide logging, set to false for writing log to STDOUT only")
|
enableLog = flag.Bool("enablelog", true, "Enable system wide logging, set to false for writing log to STDOUT only")
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ require (
|
|||||||
github.com/gorilla/sessions v1.2.2
|
github.com/gorilla/sessions v1.2.2
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.1
|
||||||
github.com/grandcat/zeroconf v1.0.0
|
github.com/grandcat/zeroconf v1.0.0
|
||||||
|
github.com/jellydator/ttlcache/v3 v3.4.0
|
||||||
github.com/likexian/whois v1.15.1
|
github.com/likexian/whois v1.15.1
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/monperrus/crawler-user-agents v1.1.0
|
github.com/monperrus/crawler-user-agents v1.1.0
|
||||||
|
|||||||
@@ -445,6 +445,8 @@ github.com/infobloxopen/infoblox-go-client/v2 v2.10.0/go.mod h1:NeNJpz09efw/edzq
|
|||||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||||
github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
github.com/jarcoal/httpmock v1.4.0 h1:BvhqnH0JAYbNudL2GMJKgOHe2CtKlzJ/5rWKyp+hc2k=
|
||||||
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
github.com/jarcoal/httpmock v1.4.0/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
|
||||||
|
github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
|
||||||
|
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jellydator/ttlcache/v3"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"imuslab.com/zoraxy/mod/database"
|
"imuslab.com/zoraxy/mod/database"
|
||||||
"imuslab.com/zoraxy/mod/info/logger"
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
@@ -22,8 +24,11 @@ type OAuth2RouterOptions struct {
|
|||||||
OAuth2WellKnownUrl string //The well-known url for OAuth 2.0 server
|
OAuth2WellKnownUrl string //The well-known url for OAuth 2.0 server
|
||||||
OAuth2UserInfoUrl string //The URL of the OAuth 2.0 user info endpoint
|
OAuth2UserInfoUrl string //The URL of the OAuth 2.0 user info endpoint
|
||||||
OAuth2Scopes string //The scopes for OAuth 2.0 Application
|
OAuth2Scopes string //The scopes for OAuth 2.0 Application
|
||||||
|
OAuth2CodeChallengeMethod string //The authorization code challange method
|
||||||
Logger *logger.Logger
|
Logger *logger.Logger
|
||||||
Database *database.Database
|
Database *database.Database
|
||||||
|
OAuth2ConfigCacheTTL *time.Duration
|
||||||
|
OAuth2ConfigCache *ttlcache.Cache[string, *oauth2.Config]
|
||||||
}
|
}
|
||||||
|
|
||||||
type OIDCDiscoveryDocument struct {
|
type OIDCDiscoveryDocument struct {
|
||||||
@@ -57,11 +62,19 @@ func NewOAuth2Router(options *OAuth2RouterOptions) *OAuth2Router {
|
|||||||
options.Database.Read("oauth2", "oauth2ClientId", &options.OAuth2ClientId)
|
options.Database.Read("oauth2", "oauth2ClientId", &options.OAuth2ClientId)
|
||||||
options.Database.Read("oauth2", "oauth2ClientSecret", &options.OAuth2ClientSecret)
|
options.Database.Read("oauth2", "oauth2ClientSecret", &options.OAuth2ClientSecret)
|
||||||
options.Database.Read("oauth2", "oauth2UserInfoUrl", &options.OAuth2UserInfoUrl)
|
options.Database.Read("oauth2", "oauth2UserInfoUrl", &options.OAuth2UserInfoUrl)
|
||||||
|
options.Database.Read("oauth2", "oauth2CodeChallengeMethod", &options.OAuth2CodeChallengeMethod)
|
||||||
options.Database.Read("oauth2", "oauth2Scopes", &options.OAuth2Scopes)
|
options.Database.Read("oauth2", "oauth2Scopes", &options.OAuth2Scopes)
|
||||||
|
|
||||||
return &OAuth2Router{
|
ar := &OAuth2Router{
|
||||||
options: options,
|
options: options,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.OAuth2ConfigCache = ttlcache.New[string, *oauth2.Config](
|
||||||
|
ttlcache.WithTTL[string, *oauth2.Config](*options.OAuth2ConfigCacheTTL),
|
||||||
|
)
|
||||||
|
go options.OAuth2ConfigCache.Start()
|
||||||
|
|
||||||
|
return ar
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleSetOAuth2Settings is the internal handler for setting the OAuth URL and HTTPS
|
// HandleSetOAuth2Settings is the internal handler for setting the OAuth URL and HTTPS
|
||||||
@@ -88,6 +101,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsGET(w http.ResponseWriter, r *http
|
|||||||
"oauth2Scopes": ar.options.OAuth2Scopes,
|
"oauth2Scopes": ar.options.OAuth2Scopes,
|
||||||
"oauth2ClientSecret": ar.options.OAuth2ClientSecret,
|
"oauth2ClientSecret": ar.options.OAuth2ClientSecret,
|
||||||
"oauth2ClientId": ar.options.OAuth2ClientId,
|
"oauth2ClientId": ar.options.OAuth2ClientId,
|
||||||
|
"oauth2CodeChallengeMethod": ar.options.OAuth2CodeChallengeMethod,
|
||||||
})
|
})
|
||||||
|
|
||||||
utils.SendJSONResponse(w, string(js))
|
utils.SendJSONResponse(w, string(js))
|
||||||
@@ -95,7 +109,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsGET(w http.ResponseWriter, r *http
|
|||||||
|
|
||||||
func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *http.Request) {
|
func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *http.Request) {
|
||||||
//Update the settings
|
//Update the settings
|
||||||
var oauth2ServerUrl, oauth2TokenURL, oauth2Scopes, oauth2UserInfoUrl string
|
var oauth2ServerUrl, oauth2TokenURL, oauth2Scopes, oauth2UserInfoUrl, oauth2CodeChallengeMethod string
|
||||||
|
|
||||||
oauth2ClientId, err := utils.PostPara(r, "oauth2ClientId")
|
oauth2ClientId, err := utils.PostPara(r, "oauth2ClientId")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,6 +123,12 @@ func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *htt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oauth2CodeChallengeMethod, err = utils.PostPara(r, "oauth2CodeChallengeMethod")
|
||||||
|
if err != nil {
|
||||||
|
utils.SendErrorResponse(w, "oauth2CodeChallengeMethod not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
oauth2WellKnownUrl, err := utils.PostPara(r, "oauth2WellKnownUrl")
|
oauth2WellKnownUrl, err := utils.PostPara(r, "oauth2WellKnownUrl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
oauth2ServerUrl, err = utils.PostPara(r, "oauth2ServerUrl")
|
oauth2ServerUrl, err = utils.PostPara(r, "oauth2ServerUrl")
|
||||||
@@ -146,6 +166,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *htt
|
|||||||
ar.options.OAuth2ClientId = oauth2ClientId
|
ar.options.OAuth2ClientId = oauth2ClientId
|
||||||
ar.options.OAuth2ClientSecret = oauth2ClientSecret
|
ar.options.OAuth2ClientSecret = oauth2ClientSecret
|
||||||
ar.options.OAuth2Scopes = oauth2Scopes
|
ar.options.OAuth2Scopes = oauth2Scopes
|
||||||
|
ar.options.OAuth2CodeChallengeMethod = oauth2CodeChallengeMethod
|
||||||
|
|
||||||
//Write changes to database
|
//Write changes to database
|
||||||
ar.options.Database.Write("oauth2", "oauth2WellKnownUrl", oauth2WellKnownUrl)
|
ar.options.Database.Write("oauth2", "oauth2WellKnownUrl", oauth2WellKnownUrl)
|
||||||
@@ -155,6 +176,10 @@ func (ar *OAuth2Router) handleSetOAuthSettingsPOST(w http.ResponseWriter, r *htt
|
|||||||
ar.options.Database.Write("oauth2", "oauth2ClientId", oauth2ClientId)
|
ar.options.Database.Write("oauth2", "oauth2ClientId", oauth2ClientId)
|
||||||
ar.options.Database.Write("oauth2", "oauth2ClientSecret", oauth2ClientSecret)
|
ar.options.Database.Write("oauth2", "oauth2ClientSecret", oauth2ClientSecret)
|
||||||
ar.options.Database.Write("oauth2", "oauth2Scopes", oauth2Scopes)
|
ar.options.Database.Write("oauth2", "oauth2Scopes", oauth2Scopes)
|
||||||
|
ar.options.Database.Write("oauth2", "oauth2CodeChallengeMethod", oauth2CodeChallengeMethod)
|
||||||
|
|
||||||
|
// Flush caches
|
||||||
|
ar.options.OAuth2ConfigCache.DeleteAll()
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@@ -167,6 +192,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsDELETE(w http.ResponseWriter, r *h
|
|||||||
ar.options.OAuth2ClientId = ""
|
ar.options.OAuth2ClientId = ""
|
||||||
ar.options.OAuth2ClientSecret = ""
|
ar.options.OAuth2ClientSecret = ""
|
||||||
ar.options.OAuth2Scopes = ""
|
ar.options.OAuth2Scopes = ""
|
||||||
|
ar.options.OAuth2CodeChallengeMethod = ""
|
||||||
|
|
||||||
ar.options.Database.Delete("oauth2", "oauth2WellKnownUrl")
|
ar.options.Database.Delete("oauth2", "oauth2WellKnownUrl")
|
||||||
ar.options.Database.Delete("oauth2", "oauth2ServerUrl")
|
ar.options.Database.Delete("oauth2", "oauth2ServerUrl")
|
||||||
@@ -175,6 +201,7 @@ func (ar *OAuth2Router) handleSetOAuthSettingsDELETE(w http.ResponseWriter, r *h
|
|||||||
ar.options.Database.Delete("oauth2", "oauth2ClientId")
|
ar.options.Database.Delete("oauth2", "oauth2ClientId")
|
||||||
ar.options.Database.Delete("oauth2", "oauth2ClientSecret")
|
ar.options.Database.Delete("oauth2", "oauth2ClientSecret")
|
||||||
ar.options.Database.Delete("oauth2", "oauth2Scopes")
|
ar.options.Database.Delete("oauth2", "oauth2Scopes")
|
||||||
|
ar.options.Database.Delete("oauth2", "oauth2CodeChallengeMethod")
|
||||||
|
|
||||||
utils.SendOK(w)
|
utils.SendOK(w)
|
||||||
}
|
}
|
||||||
@@ -189,12 +216,10 @@ func (ar *OAuth2Router) fetchOAuth2Configuration(config *oauth2.Config) (*oauth2
|
|||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
oidcDiscoveryDocument := OIDCDiscoveryDocument{}
|
oidcDiscoveryDocument := OIDCDiscoveryDocument{}
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&oidcDiscoveryDocument); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&oidcDiscoveryDocument); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.Scopes) == 0 {
|
if len(config.Scopes) == 0 {
|
||||||
config.Scopes = oidcDiscoveryDocument.ScopesSupported
|
config.Scopes = oidcDiscoveryDocument.ScopesSupported
|
||||||
}
|
}
|
||||||
@@ -241,14 +266,24 @@ func (ar *OAuth2Router) newOAuth2Conf(redirectUrl string) (*oauth2.Config, error
|
|||||||
func (ar *OAuth2Router) HandleOAuth2Auth(w http.ResponseWriter, r *http.Request) error {
|
func (ar *OAuth2Router) HandleOAuth2Auth(w http.ResponseWriter, r *http.Request) error {
|
||||||
const callbackPrefix = "/internal/oauth2"
|
const callbackPrefix = "/internal/oauth2"
|
||||||
const tokenCookie = "z-token"
|
const tokenCookie = "z-token"
|
||||||
|
const verifierCookie = "z-verifier"
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
reqUrl := scheme + "://" + r.Host + r.RequestURI
|
reqUrl := scheme + "://" + r.Host + r.RequestURI
|
||||||
|
oauthConfigCache, status := ar.options.OAuth2ConfigCache.GetOrSetFunc(r.Host, func() *oauth2.Config {
|
||||||
oauthConfig, err := ar.newOAuth2Conf(scheme + "://" + r.Host + callbackPrefix)
|
oauthConfig, err := ar.newOAuth2Conf(scheme + "://" + r.Host + callbackPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ar.options.Logger.PrintAndLog("OAuth2Router", "Failed to fetch OIDC configuration:", err)
|
ar.options.Logger.PrintAndLog("OAuth2Router", "Failed to fetch OIDC configuration:", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return oauthConfig
|
||||||
|
})
|
||||||
|
|
||||||
|
oauthConfig := oauthConfigCache.Value()
|
||||||
|
if !status {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return errors.New("failed to fetch OIDC configuration")
|
return errors.New("failed to fetch OIDC configuration")
|
||||||
}
|
}
|
||||||
@@ -263,26 +298,44 @@ func (ar *OAuth2Router) HandleOAuth2Auth(w http.ResponseWriter, r *http.Request)
|
|||||||
state := r.URL.Query().Get("state")
|
state := r.URL.Query().Get("state")
|
||||||
if r.Method == http.MethodGet && strings.HasPrefix(r.RequestURI, callbackPrefix) && code != "" && state != "" {
|
if r.Method == http.MethodGet && strings.HasPrefix(r.RequestURI, callbackPrefix) && code != "" && state != "" {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
token, err := oauthConfig.Exchange(ctx, code)
|
var authCodeOptions []oauth2.AuthCodeOption
|
||||||
|
if ar.options.OAuth2CodeChallengeMethod == "PKCE" || ar.options.OAuth2CodeChallengeMethod == "PKCE_S256" {
|
||||||
|
verifierCookie, err := r.Cookie(verifierCookie)
|
||||||
|
if err != nil || verifierCookie.Value == "" {
|
||||||
|
ar.options.Logger.PrintAndLog("OAuth2Router", "Read OAuth2 verifier cookie failed", err)
|
||||||
|
w.WriteHeader(401)
|
||||||
|
return errors.New("unauthorized")
|
||||||
|
}
|
||||||
|
authCodeOptions = append(authCodeOptions, oauth2.VerifierOption(verifierCookie.Value))
|
||||||
|
}
|
||||||
|
token, err := oauthConfig.Exchange(ctx, code, authCodeOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ar.options.Logger.PrintAndLog("OAuth2", "Token exchange failed", err)
|
ar.options.Logger.PrintAndLog("OAuth2", "Token exchange failed", err)
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
return errors.New("unauthorized")
|
return errors.New("unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !token.Valid() {
|
if !token.Valid() {
|
||||||
ar.options.Logger.PrintAndLog("OAuth2", "Invalid token", err)
|
ar.options.Logger.PrintAndLog("OAuth2", "Invalid token", err)
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(401)
|
||||||
return errors.New("unauthorized")
|
return errors.New("unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie := http.Cookie{Name: tokenCookie, Value: token.AccessToken, Path: "/"}
|
cookie := http.Cookie{Name: tokenCookie, Value: token.AccessToken, Path: "/", Expires: token.Expiry}
|
||||||
if scheme == "https" {
|
if scheme == "https" {
|
||||||
cookie.Secure = true
|
cookie.Secure = true
|
||||||
cookie.SameSite = http.SameSiteLaxMode
|
cookie.SameSite = http.SameSiteLaxMode
|
||||||
}
|
}
|
||||||
w.Header().Add("Set-Cookie", cookie.String())
|
w.Header().Add("Set-Cookie", cookie.String())
|
||||||
|
|
||||||
|
if ar.options.OAuth2CodeChallengeMethod == "PKCE" || ar.options.OAuth2CodeChallengeMethod == "PKCE_S256" {
|
||||||
|
cookie := http.Cookie{Name: verifierCookie, Value: "", Path: "/", Expires: time.Now().Add(-time.Hour * 1)}
|
||||||
|
if scheme == "https" {
|
||||||
|
cookie.Secure = true
|
||||||
|
cookie.SameSite = http.SameSiteLaxMode
|
||||||
|
}
|
||||||
|
w.Header().Add("Set-Cookie", cookie.String())
|
||||||
|
}
|
||||||
|
|
||||||
//Fix for #695
|
//Fix for #695
|
||||||
location := strings.TrimPrefix(state, "/internal/")
|
location := strings.TrimPrefix(state, "/internal/")
|
||||||
//Check if the location starts with http:// or https://. if yes, this is full URL
|
//Check if the location starts with http:// or https://. if yes, this is full URL
|
||||||
@@ -321,7 +374,25 @@ func (ar *OAuth2Router) HandleOAuth2Auth(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
if unauthorized {
|
if unauthorized {
|
||||||
state := url.QueryEscape(reqUrl)
|
state := url.QueryEscape(reqUrl)
|
||||||
url := oauthConfig.AuthCodeURL(state, oauth2.AccessTypeOffline)
|
var url string
|
||||||
|
if ar.options.OAuth2CodeChallengeMethod == "PKCE" || ar.options.OAuth2CodeChallengeMethod == "PKCE_S256" {
|
||||||
|
cookie := http.Cookie{Name: verifierCookie, Value: oauth2.GenerateVerifier(), Path: "/", Expires: time.Now().Add(time.Hour * 1)}
|
||||||
|
if scheme == "https" {
|
||||||
|
cookie.Secure = true
|
||||||
|
cookie.SameSite = http.SameSiteLaxMode
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add("Set-Cookie", cookie.String())
|
||||||
|
|
||||||
|
if ar.options.OAuth2CodeChallengeMethod == "PKCE" {
|
||||||
|
url = oauthConfig.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("code_challenge", cookie.Value))
|
||||||
|
} else {
|
||||||
|
url = oauthConfig.AuthCodeURL(state, oauth2.AccessTypeOffline, oauth2.S256ChallengeOption(cookie.Value))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
url = oauthConfig.AuthCodeURL(state, oauth2.AccessTypeOffline)
|
||||||
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, url, http.StatusFound)
|
http.Redirect(w, r, url, http.StatusFound)
|
||||||
|
|
||||||
return errors.New("unauthorized")
|
return errors.New("unauthorized")
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ func startupSequence() {
|
|||||||
oauth2Router = oauth2.NewOAuth2Router(&oauth2.OAuth2RouterOptions{
|
oauth2Router = oauth2.NewOAuth2Router(&oauth2.OAuth2RouterOptions{
|
||||||
Logger: SystemWideLogger,
|
Logger: SystemWideLogger,
|
||||||
Database: sysdb,
|
Database: sysdb,
|
||||||
|
OAuth2ConfigCacheTTL: oauth2ConfigurationCache,
|
||||||
})
|
})
|
||||||
|
|
||||||
//Create a statistic collector
|
//Create a statistic collector
|
||||||
|
|||||||
@@ -109,6 +109,26 @@
|
|||||||
<input type="password" id="oauth2ClientSecret" name="oauth2ClientSecret" placeholder="Enter Client Secret">
|
<input type="password" id="oauth2ClientSecret" name="oauth2ClientSecret" placeholder="Enter Client Secret">
|
||||||
<small>Secret key of the OAuth2 application</small>
|
<small>Secret key of the OAuth2 application</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="oauth2CodeChallengeMethod">Code Challange Method</label>
|
||||||
|
|
||||||
|
<div class="ui selection dropdown" id="oauth2CodeChallengeMethod">
|
||||||
|
<input type="hidden" name="oauth2CodeChallengeMethod">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
<div class="default text">Plain</div>
|
||||||
|
<div class="menu">
|
||||||
|
<div class="item" data-value="plain">Plain</div>
|
||||||
|
<div class="item" data-value="PKCE">PKCE</div>
|
||||||
|
<div class="item" data-value="PKCE_S256">PKCE (S256)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Options: <br>
|
||||||
|
Plain: No code challenge is used.<br>
|
||||||
|
PKCE: Uses a code challenge for added security.<br>
|
||||||
|
PKCE (S256): Uses a hashed code challenge (SHA-256) for maximum protection.<br>
|
||||||
|
<strong>Note:</strong> PKCE (especially S256) is recommended for better security.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="oauth2WellKnownUrl">Discovery URL</label>
|
<label for="oauth2WellKnownUrl">Discovery URL</label>
|
||||||
<input type="text" id="oauth2WellKnownUrl" name="oauth2WellKnownUrl" placeholder="Enter Well-Known URL">
|
<input type="text" id="oauth2WellKnownUrl" name="oauth2WellKnownUrl" placeholder="Enter Well-Known URL">
|
||||||
@@ -299,6 +319,7 @@
|
|||||||
$('#oauth2ClientId').val(data.oauth2ClientId);
|
$('#oauth2ClientId').val(data.oauth2ClientId);
|
||||||
$('#oauth2ClientSecret').val(data.oauth2ClientSecret);
|
$('#oauth2ClientSecret').val(data.oauth2ClientSecret);
|
||||||
$('#oauth2Scopes').val(data.oauth2Scopes);
|
$('#oauth2Scopes').val(data.oauth2Scopes);
|
||||||
|
$('[data-value="'+data.oauth2CodeChallengeMethod+'"]').click();
|
||||||
},
|
},
|
||||||
error: function(jqXHR, textStatus, errorThrown) {
|
error: function(jqXHR, textStatus, errorThrown) {
|
||||||
console.error('Error fetching SSO settings:', textStatus, errorThrown);
|
console.error('Error fetching SSO settings:', textStatus, errorThrown);
|
||||||
|
|||||||
@@ -408,8 +408,10 @@
|
|||||||
$("#kidInput").show();
|
$("#kidInput").show();
|
||||||
$("#hmacInput").show();
|
$("#hmacInput").show();
|
||||||
$("#skipTLS").show();
|
$("#skipTLS").show();
|
||||||
$("#dnsChallenge").hide();
|
$("#dnsChallenge").show();
|
||||||
$(".dnsChallengeOnly").hide();
|
if ($("#useDnsChallenge")[0].checked){
|
||||||
|
$(".dnsChallengeOnly").show();
|
||||||
|
}
|
||||||
} else if (this.value == "ZeroSSL") {
|
} else if (this.value == "ZeroSSL") {
|
||||||
$("#kidInput").show();
|
$("#kidInput").show();
|
||||||
$("#hmacInput").show();
|
$("#hmacInput").show();
|
||||||
|
|||||||
Reference in New Issue
Block a user