Merge pull request #65 from lsst/jwt_bearer_passthrough
JWT bearer passthrough
This commit is contained in:
commit
317f09f41e
@ -14,6 +14,10 @@
|
|||||||
|
|
||||||
## Changes since v3.2.0
|
## Changes since v3.2.0
|
||||||
|
|
||||||
|
- [#65](https://github.com/pusher/oauth2_proxy/pull/65) Improvements to authenticate requests with a JWT bearer token in the `Authorization` header via
|
||||||
|
the `-skip-jwt-bearer-token` options.
|
||||||
|
- Additional verifiers can be configured via the `-extra-jwt-issuers` flag if the JWT issuers is either an OpenID provider or has a JWKS URL
|
||||||
|
(e.g. `https://example.com/.well-known/jwks.json`).
|
||||||
- [#180](https://github.com/pusher/outh2_proxy/pull/180) Minor refactor of core proxying path (@aeijdenberg).
|
- [#180](https://github.com/pusher/outh2_proxy/pull/180) Minor refactor of core proxying path (@aeijdenberg).
|
||||||
- [#175](https://github.com/pusher/outh2_proxy/pull/175) Bump go-oidc to v2.0.0 (@aeijdenberg).
|
- [#175](https://github.com/pusher/outh2_proxy/pull/175) Bump go-oidc to v2.0.0 (@aeijdenberg).
|
||||||
- Includes fix for potential signature checking issue when OIDC discovery is skipped.
|
- Includes fix for potential signature checking issue when OIDC discovery is skipped.
|
||||||
@ -56,7 +60,6 @@
|
|||||||
- [#111](https://github.com/pusher/oauth2_proxy/pull/111) Add option for telling where to find a login.gov JWT key file (@timothy-spencer)
|
- [#111](https://github.com/pusher/oauth2_proxy/pull/111) Add option for telling where to find a login.gov JWT key file (@timothy-spencer)
|
||||||
- [#170](https://github.com/pusher/oauth2_proxy/pull/170) Restore binary tarball contents to be compatible with bitlys original tarballs (@zeha)
|
- [#170](https://github.com/pusher/oauth2_proxy/pull/170) Restore binary tarball contents to be compatible with bitlys original tarballs (@zeha)
|
||||||
- [#185](https://github.com/pusher/oauth2_proxy/pull/185) Fix an unsupported protocol scheme error during token validation when using the Azure provider (@jonas)
|
- [#185](https://github.com/pusher/oauth2_proxy/pull/185) Fix an unsupported protocol scheme error during token validation when using the Azure provider (@jonas)
|
||||||
|
|
||||||
- [#141](https://github.com/pusher/oauth2_proxy/pull/141) Check google group membership based on email address (@bchess)
|
- [#141](https://github.com/pusher/oauth2_proxy/pull/141) Check google group membership based on email address (@bchess)
|
||||||
- Google Group membership is additionally checked via email address, allowing users outside a GSuite domain to be authorized.
|
- Google Group membership is additionally checked via email address, allowing users outside a GSuite domain to be authorized.
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ Usage of oauth2_proxy:
|
|||||||
-custom-templates-dir string: path to custom html templates
|
-custom-templates-dir string: path to custom html templates
|
||||||
-display-htpasswd-form: display username / password login form if an htpasswd file is provided (default true)
|
-display-htpasswd-form: display username / password login form if an htpasswd file is provided (default true)
|
||||||
-email-domain value: authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email
|
-email-domain value: authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email
|
||||||
|
-extra-jwt-issuers: if -skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)
|
||||||
-flush-interval: period between flushing response buffers when streaming responses (default "1s")
|
-flush-interval: period between flushing response buffers when streaming responses (default "1s")
|
||||||
-footer string: custom footer string. Use "-" to disable default footer.
|
-footer string: custom footer string. Use "-" to disable default footer.
|
||||||
-gcp-healthchecks: will enable /liveness_check, /readiness_check, and / (with the proper user-agent) endpoints that will make it work well with GCP App Engine and GKE Ingresses (default false)
|
-gcp-healthchecks: will enable /liveness_check, /readiness_check, and / (with the proper user-agent) endpoints that will make it work well with GCP App Engine and GKE Ingresses (default false)
|
||||||
@ -89,6 +90,7 @@ Usage of oauth2_proxy:
|
|||||||
-signature-key string: GAP-Signature request signature key (algorithm:secretkey)
|
-signature-key string: GAP-Signature request signature key (algorithm:secretkey)
|
||||||
-skip-auth-preflight: will skip authentication for OPTIONS requests
|
-skip-auth-preflight: will skip authentication for OPTIONS requests
|
||||||
-skip-auth-regex value: bypass authentication for requests path's that match (may be given multiple times)
|
-skip-auth-regex value: bypass authentication for requests path's that match (may be given multiple times)
|
||||||
|
-skip-jwt-bearer-tokens: will skip requests that have verified JWT bearer tokens
|
||||||
-skip-oidc-discovery: bypass OIDC endpoint discovery. login-url, redeem-url and oidc-jwks-url must be configured in this case
|
-skip-oidc-discovery: bypass OIDC endpoint discovery. login-url, redeem-url and oidc-jwks-url must be configured in this case
|
||||||
-skip-provider-button: will skip sign-in-page to directly reach the next step: oauth/start
|
-skip-provider-button: will skip sign-in-page to directly reach the next step: oauth/start
|
||||||
-ssl-insecure-skip-verify: skip validation of certificates presented when using HTTPS
|
-ssl-insecure-skip-verify: skip validation of certificates presented when using HTTPS
|
||||||
|
3
main.go
3
main.go
@ -23,6 +23,7 @@ func main() {
|
|||||||
whitelistDomains := StringArray{}
|
whitelistDomains := StringArray{}
|
||||||
upstreams := StringArray{}
|
upstreams := StringArray{}
|
||||||
skipAuthRegex := StringArray{}
|
skipAuthRegex := StringArray{}
|
||||||
|
jwtIssuers := StringArray{}
|
||||||
googleGroups := StringArray{}
|
googleGroups := StringArray{}
|
||||||
redisSentinelConnectionURLs := StringArray{}
|
redisSentinelConnectionURLs := StringArray{}
|
||||||
|
|
||||||
@ -48,6 +49,8 @@ func main() {
|
|||||||
flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests")
|
flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests")
|
||||||
flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS")
|
flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS")
|
||||||
flagSet.Duration("flush-interval", time.Duration(1)*time.Second, "period between response flushing when streaming responses")
|
flagSet.Duration("flush-interval", time.Duration(1)*time.Second, "period between response flushing when streaming responses")
|
||||||
|
flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)")
|
||||||
|
flagSet.Var(&jwtIssuers, "extra-jwt-issuers", "if skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)")
|
||||||
|
|
||||||
flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
flagSet.Var(&emailDomains, "email-domain", "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||||
flagSet.Var(&whitelistDomains, "whitelist-domain", "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
|
flagSet.Var(&whitelistDomains, "whitelist-domain", "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
|
||||||
|
201
oauthproxy.go
201
oauthproxy.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
b64 "encoding/base64"
|
b64 "encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-oidc"
|
||||||
"github.com/mbland/hmacauth"
|
"github.com/mbland/hmacauth"
|
||||||
"github.com/pusher/oauth2_proxy/cookie"
|
"github.com/pusher/oauth2_proxy/cookie"
|
||||||
"github.com/pusher/oauth2_proxy/logger"
|
"github.com/pusher/oauth2_proxy/logger"
|
||||||
@ -92,6 +94,8 @@ type OAuthProxy struct {
|
|||||||
PassAuthorization bool
|
PassAuthorization bool
|
||||||
skipAuthRegex []string
|
skipAuthRegex []string
|
||||||
skipAuthPreflight bool
|
skipAuthPreflight bool
|
||||||
|
skipJwtBearerTokens bool
|
||||||
|
jwtBearerVerifiers []*oidc.IDTokenVerifier
|
||||||
compiledRegex []*regexp.Regexp
|
compiledRegex []*regexp.Regexp
|
||||||
templates *template.Template
|
templates *template.Template
|
||||||
Footer string
|
Footer string
|
||||||
@ -206,6 +210,12 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
|||||||
logger.Printf("compiled skip-auth-regex => %q", u)
|
logger.Printf("compiled skip-auth-regex => %q", u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.SkipJwtBearerTokens {
|
||||||
|
logger.Printf("Skipping JWT tokens from configured OIDC issuer: %q", opts.OIDCIssuerURL)
|
||||||
|
for _, issuer := range opts.ExtraJwtIssuers {
|
||||||
|
logger.Printf("Skipping JWT tokens from extra JWT issuer: %q", issuer)
|
||||||
|
}
|
||||||
|
}
|
||||||
redirectURL := opts.redirectURL
|
redirectURL := opts.redirectURL
|
||||||
if redirectURL.Path == "" {
|
if redirectURL.Path == "" {
|
||||||
redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix)
|
redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix)
|
||||||
@ -239,25 +249,27 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
|||||||
OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
|
OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
|
||||||
AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
|
AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
|
||||||
|
|
||||||
ProxyPrefix: opts.ProxyPrefix,
|
ProxyPrefix: opts.ProxyPrefix,
|
||||||
provider: opts.provider,
|
provider: opts.provider,
|
||||||
sessionStore: opts.sessionStore,
|
sessionStore: opts.sessionStore,
|
||||||
serveMux: serveMux,
|
serveMux: serveMux,
|
||||||
redirectURL: redirectURL,
|
redirectURL: redirectURL,
|
||||||
whitelistDomains: opts.WhitelistDomains,
|
whitelistDomains: opts.WhitelistDomains,
|
||||||
skipAuthRegex: opts.SkipAuthRegex,
|
skipAuthRegex: opts.SkipAuthRegex,
|
||||||
skipAuthPreflight: opts.SkipAuthPreflight,
|
skipAuthPreflight: opts.SkipAuthPreflight,
|
||||||
compiledRegex: opts.CompiledRegex,
|
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
|
||||||
SetXAuthRequest: opts.SetXAuthRequest,
|
jwtBearerVerifiers: opts.jwtBearerVerifiers,
|
||||||
PassBasicAuth: opts.PassBasicAuth,
|
compiledRegex: opts.CompiledRegex,
|
||||||
PassUserHeaders: opts.PassUserHeaders,
|
SetXAuthRequest: opts.SetXAuthRequest,
|
||||||
BasicAuthPassword: opts.BasicAuthPassword,
|
PassBasicAuth: opts.PassBasicAuth,
|
||||||
PassAccessToken: opts.PassAccessToken,
|
PassUserHeaders: opts.PassUserHeaders,
|
||||||
SetAuthorization: opts.SetAuthorization,
|
BasicAuthPassword: opts.BasicAuthPassword,
|
||||||
PassAuthorization: opts.PassAuthorization,
|
PassAccessToken: opts.PassAccessToken,
|
||||||
SkipProviderButton: opts.SkipProviderButton,
|
SetAuthorization: opts.SetAuthorization,
|
||||||
templates: loadTemplates(opts.CustomTemplatesDir),
|
PassAuthorization: opts.PassAuthorization,
|
||||||
Footer: opts.Footer,
|
SkipProviderButton: opts.SkipProviderButton,
|
||||||
|
templates: loadTemplates(opts.CustomTemplatesDir),
|
||||||
|
Footer: opts.Footer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,7 +650,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
http.Redirect(rw, req, redirect, 302)
|
http.Redirect(rw, req, redirect, 302)
|
||||||
} else {
|
} else {
|
||||||
logger.PrintAuthf(session.Email, req, logger.AuthSuccess, "Invalid authentication via OAuth2: unauthorized")
|
logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unauthorized")
|
||||||
p.ErrorPage(rw, 403, "Permission Denied", "Invalid Account")
|
p.ErrorPage(rw, 403, "Permission Denied", "Invalid Account")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -693,26 +705,42 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||||||
// Returns nil, ErrNeedsLogin if user needs to login.
|
// Returns nil, ErrNeedsLogin if user needs to login.
|
||||||
// Set-Cookie headers may be set on the response as a side-effect of calling this method.
|
// Set-Cookie headers may be set on the response as a side-effect of calling this method.
|
||||||
func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.Request) (*sessionsapi.SessionState, error) {
|
func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.Request) (*sessionsapi.SessionState, error) {
|
||||||
|
var session *sessionsapi.SessionState
|
||||||
|
var err error
|
||||||
var saveSession, clearSession, revalidated bool
|
var saveSession, clearSession, revalidated bool
|
||||||
|
|
||||||
|
if p.skipJwtBearerTokens && req.Header.Get("Authorization") != "" {
|
||||||
|
session, err = p.GetJwtSession(req)
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Error retrieving session from token in Authorization header: %s", err)
|
||||||
|
}
|
||||||
|
if session != nil {
|
||||||
|
saveSession = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
remoteAddr := getRemoteAddr(req)
|
remoteAddr := getRemoteAddr(req)
|
||||||
|
if session == nil {
|
||||||
|
session, err = p.LoadCookiedSession(req)
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("Error loading cookied session: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
session, err := p.LoadCookiedSession(req)
|
if session != nil {
|
||||||
if err != nil {
|
if session.Age() > p.CookieRefresh && p.CookieRefresh != time.Duration(0) {
|
||||||
logger.Printf("Error loading cookied session: %s", err)
|
logger.Printf("Refreshing %s old session cookie for %s (refresh after %s)", session.Age(), session, p.CookieRefresh)
|
||||||
}
|
saveSession = true
|
||||||
if session != nil && session.Age() > p.CookieRefresh && p.CookieRefresh != time.Duration(0) {
|
}
|
||||||
logger.Printf("Refreshing %s old session cookie for %s (refresh after %s)", session.Age(), session, p.CookieRefresh)
|
|
||||||
saveSession = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var ok bool
|
if ok, err := p.provider.RefreshSessionIfNeeded(session); err != nil {
|
||||||
if ok, err = p.provider.RefreshSessionIfNeeded(session); err != nil {
|
logger.Printf("%s removing session. error refreshing access token %s %s", remoteAddr, err, session)
|
||||||
logger.Printf("%s removing session. error refreshing access token %s %s", remoteAddr, err, session)
|
clearSession = true
|
||||||
clearSession = true
|
session = nil
|
||||||
session = nil
|
} else if ok {
|
||||||
} else if ok {
|
saveSession = true
|
||||||
saveSession = true
|
revalidated = true
|
||||||
revalidated = true
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if session != nil && session.IsExpired() {
|
if session != nil && session.IsExpired() {
|
||||||
@ -731,11 +759,13 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if session != nil && session.Email != "" && !p.Validator(session.Email) {
|
if session != nil && session.Email != "" {
|
||||||
logger.Printf(session.Email, req, logger.AuthFailure, "Invalid authentication via session: removing session %s", session)
|
if !p.Validator(session.Email) || !p.provider.ValidateGroup(session.Email) {
|
||||||
session = nil
|
logger.Printf(session.Email, req, logger.AuthFailure, "Invalid authentication via session: removing session %s", session)
|
||||||
saveSession = false
|
session = nil
|
||||||
clearSession = true
|
saveSession = false
|
||||||
|
clearSession = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveSession && session != nil {
|
if saveSession && session != nil {
|
||||||
@ -854,3 +884,92 @@ func (p *OAuthProxy) ErrorJSON(rw http.ResponseWriter, code int) {
|
|||||||
rw.Header().Set("Content-Type", applicationJSON)
|
rw.Header().Set("Content-Type", applicationJSON)
|
||||||
rw.WriteHeader(code)
|
rw.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetJwtSession loads a session based on a JWT token in the authorization header.
|
||||||
|
func (p *OAuthProxy) GetJwtSession(req *http.Request) (*sessionsapi.SessionState, error) {
|
||||||
|
rawBearerToken, err := p.findBearerToken(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
var session *sessionsapi.SessionState
|
||||||
|
for _, verifier := range p.jwtBearerVerifiers {
|
||||||
|
bearerToken, err := verifier.Verify(ctx, rawBearerToken)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("failed to verify bearer token: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var claims struct {
|
||||||
|
Subject string `json:"sub"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Verified *bool `json:"email_verified"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bearerToken.Claims(&claims); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse bearer token claims: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Email == "" {
|
||||||
|
claims.Email = claims.Subject
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.Verified != nil && !*claims.Verified {
|
||||||
|
return nil, fmt.Errorf("email in id_token (%s) isn't verified", claims.Email)
|
||||||
|
}
|
||||||
|
|
||||||
|
session = &sessionsapi.SessionState{
|
||||||
|
AccessToken: rawBearerToken,
|
||||||
|
IDToken: rawBearerToken,
|
||||||
|
RefreshToken: "",
|
||||||
|
ExpiresOn: bearerToken.Expiry,
|
||||||
|
Email: claims.Email,
|
||||||
|
User: claims.Email,
|
||||||
|
}
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("unable to verify jwt token %s", req.Header.Get("Authorization"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// findBearerToken finds a valid JWT token from the Authorization header of a given request.
|
||||||
|
func (p *OAuthProxy) findBearerToken(req *http.Request) (string, error) {
|
||||||
|
auth := req.Header.Get("Authorization")
|
||||||
|
s := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(s) != 2 {
|
||||||
|
return "", fmt.Errorf("invalid authorization header %s", auth)
|
||||||
|
}
|
||||||
|
jwtRegex := regexp.MustCompile(`^eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]+$`)
|
||||||
|
var rawBearerToken string
|
||||||
|
if s[0] == "Bearer" && jwtRegex.MatchString(s[1]) {
|
||||||
|
rawBearerToken = s[1]
|
||||||
|
} else if s[0] == "Basic" {
|
||||||
|
// Check if we have a Bearer token masquerading in Basic
|
||||||
|
b, err := b64.StdEncoding.DecodeString(s[1])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
pair := strings.SplitN(string(b), ":", 2)
|
||||||
|
if len(pair) != 2 {
|
||||||
|
return "", fmt.Errorf("invalid format %s", b)
|
||||||
|
}
|
||||||
|
user, password := pair[0], pair[1]
|
||||||
|
|
||||||
|
// check user, user+password, or just password for a token
|
||||||
|
if jwtRegex.MatchString(user) {
|
||||||
|
// Support blank passwords or magic `x-oauth-basic` passwords - nothing else
|
||||||
|
if password == "" || password == "x-oauth-basic" {
|
||||||
|
rawBearerToken = user
|
||||||
|
}
|
||||||
|
} else if jwtRegex.MatchString(password) {
|
||||||
|
// support passwords and ignore user
|
||||||
|
rawBearerToken = password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rawBearerToken == "" {
|
||||||
|
return "", fmt.Errorf("no valid bearer token found in authorization header")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawBearerToken, nil
|
||||||
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
@ -14,6 +16,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/coreos/go-oidc"
|
||||||
"github.com/mbland/hmacauth"
|
"github.com/mbland/hmacauth"
|
||||||
"github.com/pusher/oauth2_proxy/logger"
|
"github.com/pusher/oauth2_proxy/logger"
|
||||||
"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
|
"github.com/pusher/oauth2_proxy/pkg/apis/sessions"
|
||||||
@ -226,8 +229,9 @@ func TestIsValidRedirect(t *testing.T) {
|
|||||||
|
|
||||||
type TestProvider struct {
|
type TestProvider struct {
|
||||||
*providers.ProviderData
|
*providers.ProviderData
|
||||||
EmailAddress string
|
EmailAddress string
|
||||||
ValidToken bool
|
ValidToken bool
|
||||||
|
GroupValidator func(string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
|
func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
|
||||||
@ -252,6 +256,9 @@ func NewTestProvider(providerURL *url.URL, emailAddress string) *TestProvider {
|
|||||||
Scope: "profile.email",
|
Scope: "profile.email",
|
||||||
},
|
},
|
||||||
EmailAddress: emailAddress,
|
EmailAddress: emailAddress,
|
||||||
|
GroupValidator: func(s string) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,6 +270,13 @@ func (tp *TestProvider) ValidateSessionState(session *sessions.SessionState) boo
|
|||||||
return tp.ValidToken
|
return tp.ValidToken
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tp *TestProvider) ValidateGroup(email string) bool {
|
||||||
|
if tp.GroupValidator != nil {
|
||||||
|
return tp.GroupValidator(email)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func TestBasicAuthPassword(t *testing.T) {
|
func TestBasicAuthPassword(t *testing.T) {
|
||||||
providerServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
providerServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
logger.Printf("%#v", r)
|
logger.Printf("%#v", r)
|
||||||
@ -788,6 +802,25 @@ func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
|
|||||||
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAuthOnlyEndpointUnauthorizedOnProviderGroupValidationFailure(t *testing.T) {
|
||||||
|
test := NewAuthOnlyEndpointTest()
|
||||||
|
startSession := &sessions.SessionState{
|
||||||
|
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token", CreatedAt: time.Now()}
|
||||||
|
test.SaveSession(startSession)
|
||||||
|
provider := &TestProvider{
|
||||||
|
ValidToken: true,
|
||||||
|
GroupValidator: func(s string) bool {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
test.proxy.provider = provider
|
||||||
|
test.proxy.ServeHTTP(test.rw, test.req)
|
||||||
|
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
|
||||||
|
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
|
||||||
|
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
|
func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
|
||||||
var pcTest ProcessCookieTest
|
var pcTest ProcessCookieTest
|
||||||
|
|
||||||
@ -1132,3 +1165,173 @@ func TestClearSingleCookie(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, 1, len(header["Set-Cookie"]), "should have 1 set-cookie header entries")
|
assert.Equal(t, 1, len(header["Set-Cookie"]), "should have 1 set-cookie header entries")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NoOpKeySet struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NoOpKeySet) VerifySignature(ctx context.Context, jwt string) (payload []byte, err error) {
|
||||||
|
splitStrings := strings.Split(jwt, ".")
|
||||||
|
payloadString := splitStrings[1]
|
||||||
|
jsonString, err := base64.RawURLEncoding.DecodeString(payloadString)
|
||||||
|
return []byte(jsonString), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetJwtSession(t *testing.T) {
|
||||||
|
/* token payload:
|
||||||
|
{
|
||||||
|
"sub": "1234567890",
|
||||||
|
"aud": "https://test.myapp.com",
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john@example.com",
|
||||||
|
"iss": "https://issuer.example.com",
|
||||||
|
"iat": 1553691215,
|
||||||
|
"exp": 1912151821
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
goodJwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||||
|
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYXVkIjoiaHR0cHM6Ly90ZXN0Lm15YXBwLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImVtY" +
|
||||||
|
"WlsIjoiam9obkBleGFtcGxlLmNvbSIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNTUzNjkxMj" +
|
||||||
|
"E1LCJleHAiOjE5MTIxNTE4MjF9." +
|
||||||
|
"rLVyzOnEldUq_pNkfa-WiV8TVJYWyZCaM2Am_uo8FGg11zD7l-qmz3x1seTvqpH6Y0Ty00fmv6dJnGnC8WMnPXQiodRTfhBSe" +
|
||||||
|
"OKZMu0HkMD2sg52zlKkbfLTO6ic5VnbVgwjjrB8am_Ta6w7kyFUaB5C1BsIrrLMldkWEhynbb8"
|
||||||
|
|
||||||
|
keyset := NoOpKeySet{}
|
||||||
|
verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
|
||||||
|
&oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
|
||||||
|
|
||||||
|
test := NewAuthOnlyEndpointTest(func(opts *Options) {
|
||||||
|
opts.PassAuthorization = true
|
||||||
|
opts.SetAuthorization = true
|
||||||
|
opts.SetXAuthRequest = true
|
||||||
|
opts.SkipJwtBearerTokens = true
|
||||||
|
opts.jwtBearerVerifiers = append(opts.jwtBearerVerifiers, verifier)
|
||||||
|
})
|
||||||
|
tp, _ := test.proxy.provider.(*TestProvider)
|
||||||
|
tp.GroupValidator = func(s string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
authHeader := fmt.Sprintf("Bearer %s", goodJwt)
|
||||||
|
test.req.Header = map[string][]string{
|
||||||
|
"Authorization": {authHeader},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bearer
|
||||||
|
session, _ := test.proxy.GetJwtSession(test.req)
|
||||||
|
assert.Equal(t, session.User, "john@example.com")
|
||||||
|
assert.Equal(t, session.Email, "john@example.com")
|
||||||
|
assert.Equal(t, session.ExpiresOn, time.Unix(1912151821, 0))
|
||||||
|
assert.Equal(t, session.IDToken, goodJwt)
|
||||||
|
|
||||||
|
test.proxy.ServeHTTP(test.rw, test.req)
|
||||||
|
if test.rw.Code >= 400 {
|
||||||
|
t.Fatalf("expected 3xx got %d", test.rw.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check PassAuthorization, should overwrite Basic header
|
||||||
|
assert.Equal(t, test.req.Header.Get("Authorization"), authHeader)
|
||||||
|
assert.Equal(t, test.req.Header.Get("X-Forwarded-User"), "john@example.com")
|
||||||
|
assert.Equal(t, test.req.Header.Get("X-Forwarded-Email"), "john@example.com")
|
||||||
|
|
||||||
|
// SetAuthorization and SetXAuthRequest
|
||||||
|
assert.Equal(t, test.rw.Header().Get("Authorization"), authHeader)
|
||||||
|
assert.Equal(t, test.rw.Header().Get("X-Auth-Request-User"), "john@example.com")
|
||||||
|
assert.Equal(t, test.rw.Header().Get("X-Auth-Request-Email"), "john@example.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJwtUnauthorizedOnGroupValidationFailure(t *testing.T) {
|
||||||
|
goodJwt := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||||
|
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYXVkIjoiaHR0cHM6Ly90ZXN0Lm15YXBwLmNvbSIsIm5hbWUiOiJKb2huIERvZSIsImVtY" +
|
||||||
|
"WlsIjoiam9obkBleGFtcGxlLmNvbSIsImlzcyI6Imh0dHBzOi8vaXNzdWVyLmV4YW1wbGUuY29tIiwiaWF0IjoxNTUzNjkxMj" +
|
||||||
|
"E1LCJleHAiOjE5MTIxNTE4MjF9." +
|
||||||
|
"rLVyzOnEldUq_pNkfa-WiV8TVJYWyZCaM2Am_uo8FGg11zD7l-qmz3x1seTvqpH6Y0Ty00fmv6dJnGnC8WMnPXQiodRTfhBSe" +
|
||||||
|
"OKZMu0HkMD2sg52zlKkbfLTO6ic5VnbVgwjjrB8am_Ta6w7kyFUaB5C1BsIrrLMldkWEhynbb8"
|
||||||
|
|
||||||
|
keyset := NoOpKeySet{}
|
||||||
|
verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
|
||||||
|
&oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
|
||||||
|
|
||||||
|
test := NewAuthOnlyEndpointTest(func(opts *Options) {
|
||||||
|
opts.PassAuthorization = true
|
||||||
|
opts.SetAuthorization = true
|
||||||
|
opts.SetXAuthRequest = true
|
||||||
|
opts.SkipJwtBearerTokens = true
|
||||||
|
opts.jwtBearerVerifiers = append(opts.jwtBearerVerifiers, verifier)
|
||||||
|
})
|
||||||
|
tp, _ := test.proxy.provider.(*TestProvider)
|
||||||
|
// Verify ValidateGroup fails JWT authorization
|
||||||
|
tp.GroupValidator = func(s string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
authHeader := fmt.Sprintf("Bearer %s", goodJwt)
|
||||||
|
test.req.Header = map[string][]string{
|
||||||
|
"Authorization": {authHeader},
|
||||||
|
}
|
||||||
|
test.proxy.ServeHTTP(test.rw, test.req)
|
||||||
|
if test.rw.Code != http.StatusUnauthorized {
|
||||||
|
t.Fatalf("expected 401 got %d", test.rw.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindJwtBearerToken(t *testing.T) {
|
||||||
|
p := OAuthProxy{CookieName: "oauth2", CookieDomain: "abc"}
|
||||||
|
getReq := &http.Request{URL: &url.URL{Scheme: "http", Host: "example.com"}}
|
||||||
|
|
||||||
|
validToken := "eyJfoobar.eyJfoobar.12345asdf"
|
||||||
|
var token string
|
||||||
|
|
||||||
|
// Bearer
|
||||||
|
getReq.Header = map[string][]string{
|
||||||
|
"Authorization": {fmt.Sprintf("Bearer %s", validToken)},
|
||||||
|
}
|
||||||
|
|
||||||
|
token, _ = p.findBearerToken(getReq)
|
||||||
|
assert.Equal(t, validToken, token)
|
||||||
|
|
||||||
|
// Basic - no password
|
||||||
|
getReq.SetBasicAuth(token, "")
|
||||||
|
token, _ = p.findBearerToken(getReq)
|
||||||
|
assert.Equal(t, validToken, token)
|
||||||
|
|
||||||
|
// Basic - sentinel password
|
||||||
|
getReq.SetBasicAuth(token, "x-oauth-basic")
|
||||||
|
token, _ = p.findBearerToken(getReq)
|
||||||
|
assert.Equal(t, validToken, token)
|
||||||
|
|
||||||
|
// Basic - any username, password matching jwt pattern
|
||||||
|
getReq.SetBasicAuth("any-username-you-could-wish-for", token)
|
||||||
|
token, _ = p.findBearerToken(getReq)
|
||||||
|
assert.Equal(t, validToken, token)
|
||||||
|
|
||||||
|
failures := []string{
|
||||||
|
// Too many parts
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Not enough parts
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Invalid encrypted key
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.//////.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Invalid IV
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.//////.dGVzdA.dGVzdA",
|
||||||
|
// Invalid ciphertext
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.//////.dGVzdA",
|
||||||
|
// Invalid tag
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.//////",
|
||||||
|
// Invalid header
|
||||||
|
"W10.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Invalid header
|
||||||
|
"######.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Missing alc/enc params
|
||||||
|
"e30.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, failure := range failures {
|
||||||
|
getReq.Header = map[string][]string{
|
||||||
|
"Authorization": {fmt.Sprintf("Bearer %s", failure)},
|
||||||
|
}
|
||||||
|
_, err := p.findBearerToken(getReq)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s", token)
|
||||||
|
}
|
||||||
|
81
options.go
81
options.go
@ -61,6 +61,8 @@ type Options struct {
|
|||||||
|
|
||||||
Upstreams []string `flag:"upstream" cfg:"upstreams" env:"OAUTH2_PROXY_UPSTREAMS"`
|
Upstreams []string `flag:"upstream" cfg:"upstreams" env:"OAUTH2_PROXY_UPSTREAMS"`
|
||||||
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex" env:"OAUTH2_PROXY_SKIP_AUTH_REGEX"`
|
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex" env:"OAUTH2_PROXY_SKIP_AUTH_REGEX"`
|
||||||
|
SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens" env:"OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS"`
|
||||||
|
ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers" env:"OAUTH2_PROXY_EXTRA_JWT_ISSUERS"`
|
||||||
PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth" env:"OAUTH2_PROXY_PASS_BASIC_AUTH"`
|
PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth" env:"OAUTH2_PROXY_PASS_BASIC_AUTH"`
|
||||||
BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password" env:"OAUTH2_PROXY_BASIC_AUTH_PASSWORD"`
|
BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password" env:"OAUTH2_PROXY_BASIC_AUTH_PASSWORD"`
|
||||||
PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token" env:"OAUTH2_PROXY_PASS_ACCESS_TOKEN"`
|
PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token" env:"OAUTH2_PROXY_PASS_ACCESS_TOKEN"`
|
||||||
@ -110,13 +112,14 @@ type Options struct {
|
|||||||
GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"`
|
GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"`
|
||||||
|
|
||||||
// internal values that are set after config validation
|
// internal values that are set after config validation
|
||||||
redirectURL *url.URL
|
redirectURL *url.URL
|
||||||
proxyURLs []*url.URL
|
proxyURLs []*url.URL
|
||||||
CompiledRegex []*regexp.Regexp
|
CompiledRegex []*regexp.Regexp
|
||||||
provider providers.Provider
|
provider providers.Provider
|
||||||
sessionStore sessionsapi.SessionStore
|
sessionStore sessionsapi.SessionStore
|
||||||
signatureData *SignatureData
|
signatureData *SignatureData
|
||||||
oidcVerifier *oidc.IDTokenVerifier
|
oidcVerifier *oidc.IDTokenVerifier
|
||||||
|
jwtBearerVerifiers []*oidc.IDTokenVerifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignatureData holds hmacauth signature hash and key
|
// SignatureData holds hmacauth signature hash and key
|
||||||
@ -168,6 +171,12 @@ func NewOptions() *Options {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.
|
||||||
|
type jwtIssuer struct {
|
||||||
|
issuerURI string
|
||||||
|
audience string
|
||||||
|
}
|
||||||
|
|
||||||
func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
|
func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
|
||||||
parsed, err := url.Parse(toParse)
|
parsed, err := url.Parse(toParse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -244,6 +253,25 @@ func (o *Options) Validate() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.SkipJwtBearerTokens {
|
||||||
|
// If we are using an oidc provider, go ahead and add that provider to the list
|
||||||
|
if o.oidcVerifier != nil {
|
||||||
|
o.jwtBearerVerifiers = append(o.jwtBearerVerifiers, o.oidcVerifier)
|
||||||
|
}
|
||||||
|
// Configure extra issuers
|
||||||
|
if len(o.ExtraJwtIssuers) > 0 {
|
||||||
|
var jwtIssuers []jwtIssuer
|
||||||
|
jwtIssuers, msgs = parseJwtIssuers(o.ExtraJwtIssuers, msgs)
|
||||||
|
for _, jwtIssuer := range jwtIssuers {
|
||||||
|
verifier, err := newVerifierFromJwtIssuer(jwtIssuer)
|
||||||
|
if err != nil {
|
||||||
|
msgs = append(msgs, fmt.Sprintf("error building verifiers: %s", err))
|
||||||
|
}
|
||||||
|
o.jwtBearerVerifiers = append(o.jwtBearerVerifiers, verifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs)
|
o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs)
|
||||||
|
|
||||||
for _, u := range o.Upstreams {
|
for _, u := range o.Upstreams {
|
||||||
@ -430,6 +458,45 @@ func parseSignatureKey(o *Options, msgs []string) []string {
|
|||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseJwtIssuers takes in an array of strings in the form of issuer=audience
|
||||||
|
// and parses to an array of jwtIssuer structs.
|
||||||
|
func parseJwtIssuers(issuers []string, msgs []string) ([]jwtIssuer, []string) {
|
||||||
|
var parsedIssuers []jwtIssuer
|
||||||
|
for _, jwtVerifier := range issuers {
|
||||||
|
components := strings.Split(jwtVerifier, "=")
|
||||||
|
if len(components) < 2 {
|
||||||
|
msgs = append(msgs, fmt.Sprintf("invalid jwt verifier uri=audience spec: %s", jwtVerifier))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uri, audience := components[0], strings.Join(components[1:], "=")
|
||||||
|
parsedIssuers = append(parsedIssuers, jwtIssuer{issuerURI: uri, audience: audience})
|
||||||
|
}
|
||||||
|
return parsedIssuers, msgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// newVerifierFromJwtIssuer takes in issuer information in jwtIssuer info and returns
|
||||||
|
// a verifier for that issuer.
|
||||||
|
func newVerifierFromJwtIssuer(jwtIssuer jwtIssuer) (*oidc.IDTokenVerifier, error) {
|
||||||
|
config := &oidc.Config{
|
||||||
|
ClientID: jwtIssuer.audience,
|
||||||
|
}
|
||||||
|
// Try as an OpenID Connect Provider first
|
||||||
|
var verifier *oidc.IDTokenVerifier
|
||||||
|
provider, err := oidc.NewProvider(context.Background(), jwtIssuer.issuerURI)
|
||||||
|
if err != nil {
|
||||||
|
// Try as JWKS URI
|
||||||
|
jwksURI := strings.TrimSuffix(jwtIssuer.issuerURI, "/") + "/.well-known/jwks.json"
|
||||||
|
_, err := http.NewRequest("GET", jwksURI, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
verifier = oidc.NewVerifier(jwtIssuer.issuerURI, oidc.NewRemoteKeySet(context.Background(), jwksURI), config)
|
||||||
|
} else {
|
||||||
|
verifier = provider.Verifier(config)
|
||||||
|
}
|
||||||
|
return verifier, nil
|
||||||
|
}
|
||||||
|
|
||||||
func validateCookieName(o *Options, msgs []string) []string {
|
func validateCookieName(o *Options, msgs []string) []string {
|
||||||
cookie := &http.Cookie{Name: o.CookieName}
|
cookie := &http.Cookie{Name: o.CookieName}
|
||||||
if cookie.String() == "" {
|
if cookie.String() == "" {
|
||||||
|
@ -128,7 +128,7 @@ func (p *OIDCProvider) createSessionState(ctx context.Context, token *oauth2.Tok
|
|||||||
IDToken: rawIDToken,
|
IDToken: rawIDToken,
|
||||||
RefreshToken: token.RefreshToken,
|
RefreshToken: token.RefreshToken,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
ExpiresOn: token.Expiry,
|
ExpiresOn: idToken.Expiry,
|
||||||
Email: claims.Email,
|
Email: claims.Email,
|
||||||
User: claims.Subject,
|
User: claims.Subject,
|
||||||
}, nil
|
}, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user