mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-28 18:06:34 +02:00
wip
This commit is contained in:
parent
229ef72e58
commit
a8650b1749
13 changed files with 465 additions and 25 deletions
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -95,6 +96,8 @@ func (a *Authenticate) mountDashboard(r *mux.Router) {
|
|||
// routes that don't need a session:
|
||||
sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut))
|
||||
sr.Path("/signed_out").Handler(httputil.HandlerFunc(a.signedOut)).Methods(http.MethodGet)
|
||||
sr.Path("/verify-access-token").Handler(httputil.HandlerFunc(a.verifyAccessToken)).Methods(http.MethodPost)
|
||||
sr.Path("/verify-identity-token").Handler(httputil.HandlerFunc(a.verifyIdentityToken)).Methods(http.MethodPost)
|
||||
|
||||
// routes that need a session:
|
||||
sr = sr.NewRoute().Subrouter()
|
||||
|
@ -568,3 +571,83 @@ func (a *Authenticate) getIdentityProviderIDForRequest(r *http.Request) string {
|
|||
}
|
||||
return a.state.Load().flow.GetIdentityProviderIDForURLValues(r.Form)
|
||||
}
|
||||
|
||||
type VerifyAccessTokenRequest struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
IdentityProviderID string `json:"identityProviderId,omitempty"`
|
||||
}
|
||||
|
||||
type VerifyIdentityTokenRequest struct {
|
||||
IdentityToken string `json:"identityToken"`
|
||||
IdentityProviderID string `json:"identityProviderId,omitempty"`
|
||||
}
|
||||
|
||||
type VerifyTokenResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Claims map[string]any `json:"claims,omitempty"`
|
||||
}
|
||||
|
||||
func (a *Authenticate) verifyAccessToken(w http.ResponseWriter, r *http.Request) error {
|
||||
// TODO: implement authorization
|
||||
|
||||
var req VerifyAccessTokenRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return httputil.NewError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
authenticator, err := a.cfg.getIdentityProvider(r.Context(), a.tracerProvider, a.options.Load(), req.IdentityProviderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var res VerifyTokenResponse
|
||||
claims, err := authenticator.VerifyAccessToken(r.Context(), req.AccessToken)
|
||||
if err == nil {
|
||||
res.Valid = true
|
||||
res.Claims = claims
|
||||
} else {
|
||||
res.Valid = false
|
||||
res.Error = err.Error()
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(&res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Authenticate) verifyIdentityToken(w http.ResponseWriter, r *http.Request) error {
|
||||
// TODO: implement authorization
|
||||
|
||||
var req VerifyIdentityTokenRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return httputil.NewError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
authenticator, err := a.cfg.getIdentityProvider(r.Context(), a.tracerProvider, a.options.Load(), req.IdentityProviderID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var res VerifyTokenResponse
|
||||
claims, err := authenticator.VerifyIdentityToken(r.Context(), req.IdentityToken)
|
||||
if err == nil {
|
||||
res.Valid = true
|
||||
res.Claims = claims
|
||||
} else {
|
||||
res.Valid = false
|
||||
res.Error = err.Error()
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(&res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,15 +3,17 @@ package authorize
|
|||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type sessionOrServiceAccount interface {
|
||||
GetId() string
|
||||
GetUserId() string
|
||||
Validate() error
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package authorize
|
|||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -44,26 +45,29 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
|
|||
requestID := requestid.FromHTTPHeader(hreq.Header)
|
||||
ctx = requestid.WithValue(ctx, requestID)
|
||||
|
||||
sessionState, _ := state.sessionStore.LoadSessionStateAndCheckIDP(hreq)
|
||||
|
||||
var s sessionOrServiceAccount
|
||||
var u *user.User
|
||||
var err error
|
||||
if sessionState != nil {
|
||||
s, err = a.getDataBrokerSessionOrServiceAccount(ctx, sessionState.ID, sessionState.DatabrokerRecordVersion)
|
||||
if status.Code(err) == codes.Unavailable {
|
||||
log.Ctx(ctx).Debug().Str("request-id", requestID).Err(err).Msg("temporary error checking authorization: data broker unavailable")
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
log.Ctx(ctx).Info().Err(err).Str("request-id", requestID).Msg("clearing session due to missing or invalid session or service account")
|
||||
sessionState = nil
|
||||
if sess, err := a.state.Load().idpTokensLoader.LoadSession(hreq); err == nil {
|
||||
s = sess
|
||||
} else if !errors.Is(err, sessions.ErrNoSessionFound) {
|
||||
log.Ctx(ctx).Info().Err(err).Str("request-id", requestID).Msg("error verifying idp tokens")
|
||||
} else {
|
||||
sessionState, _ := state.sessionStore.LoadSessionStateAndCheckIDP(hreq)
|
||||
if sessionState != nil {
|
||||
s, err = a.getDataBrokerSessionOrServiceAccount(ctx, sessionState.ID, sessionState.DatabrokerRecordVersion)
|
||||
if status.Code(err) == codes.Unavailable {
|
||||
log.Ctx(ctx).Debug().Str("request-id", requestID).Err(err).Msg("temporary error checking authorization: data broker unavailable")
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
log.Ctx(ctx).Info().Err(err).Str("request-id", requestID).Msg("clearing session due to missing or invalid session or service account")
|
||||
}
|
||||
}
|
||||
}
|
||||
if sessionState != nil && s != nil {
|
||||
if s != nil {
|
||||
u, _ = a.getDataBrokerUser(ctx, s.GetUserId()) // ignore any missing user error
|
||||
}
|
||||
|
||||
req, err := a.getEvaluatorRequestFromCheckRequest(ctx, in, sessionState)
|
||||
req, err := a.getEvaluatorRequestFromCheckRequest(ctx, in, s.GetId())
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Str("request-id", requestID).Msg("error building evaluator request")
|
||||
return nil, err
|
||||
|
@ -91,7 +95,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
|
|||
func (a *Authorize) getEvaluatorRequestFromCheckRequest(
|
||||
ctx context.Context,
|
||||
in *envoy_service_auth_v3.CheckRequest,
|
||||
sessionState *sessions.State,
|
||||
sessionID string,
|
||||
) (*evaluator.Request, error) {
|
||||
requestURL := getCheckRequestURL(in)
|
||||
attrs := in.GetAttributes()
|
||||
|
@ -106,9 +110,9 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
|
|||
attrs.GetSource().GetAddress().GetSocketAddress().GetAddress(),
|
||||
),
|
||||
}
|
||||
if sessionState != nil {
|
||||
if sessionID != "" {
|
||||
req.Session = evaluator.RequestSession{
|
||||
ID: sessionState.ID,
|
||||
ID: sessionID,
|
||||
}
|
||||
}
|
||||
req.Policy = a.getMatchingPolicy(envoyconfig.ExtAuthzContextExtensionsRouteID(attrs.GetContextExtensions()))
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/atomicutil"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/internal/testutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/storage"
|
||||
|
@ -88,9 +87,7 @@ func Test_getEvaluatorRequest(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
&sessions.State{
|
||||
ID: "SESSION_ID",
|
||||
},
|
||||
"SESSION_ID",
|
||||
)
|
||||
require.NoError(t, err)
|
||||
expect := &evaluator.Request{
|
||||
|
@ -145,7 +142,7 @@ func Test_getEvaluatorRequestWithPortInHostHeader(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
}, "")
|
||||
require.NoError(t, err)
|
||||
expect := &evaluator.Request{
|
||||
Policy: &a.currentOptions.Load().Policies[0],
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/authenticateflow"
|
||||
"github.com/pomerium/pomerium/internal/sessions/idptokens"
|
||||
"github.com/pomerium/pomerium/pkg/grpc"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
)
|
||||
|
@ -30,6 +31,7 @@ type authorizeState struct {
|
|||
dataBrokerClient databroker.DataBrokerServiceClient
|
||||
sessionStore *config.SessionStore
|
||||
authenticateFlow authenticateFlow
|
||||
idpTokensLoader *idptokens.Loader
|
||||
}
|
||||
|
||||
func newAuthorizeStateFromConfig(
|
||||
|
@ -88,5 +90,7 @@ func newAuthorizeStateFromConfig(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
state.idpTokensLoader = idptokens.NewLoader(cfg.Options, state.dataBrokerClient)
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package httputil
|
||||
|
||||
// AuthorizationTypePomerium is for Authorization: Pomerium JWT... headers
|
||||
const AuthorizationTypePomerium = "Pomerium"
|
||||
// Authorization types
|
||||
const (
|
||||
AuthorizationTypePomerium = "Pomerium"
|
||||
AuthorizationTypePomeriumIDPAccessToken = "Pomerium-IDP-Access-Token" //nolint: gosec
|
||||
AuthorizationTypePomeriumIDPIdentityToken = "Pomerium-IDP-Identity-Token" //nolint: gosec
|
||||
)
|
||||
|
||||
// Standard headers
|
||||
const (
|
||||
|
@ -32,6 +36,10 @@ const (
|
|||
HeaderPomeriumReproxyPolicyHMAC = "x-pomerium-reproxy-policy-hmac"
|
||||
// HeaderPomeriumRoutingKey is a string used for routing user requests to a consistent upstream server.
|
||||
HeaderPomeriumRoutingKey = "x-pomerium-routing-key"
|
||||
// HeaderPomeriumIDPAccessToken is the header key for a pomerium access token.
|
||||
HeaderPomeriumIDPAccessToken = "x-pomerium-idp-access-token"
|
||||
// HeaderPomeriumIDPIdentityToken is the header key for a pomerium identity token.
|
||||
HeaderPomeriumIDPIdentityToken = "x-pomerium-idp-identity-token"
|
||||
)
|
||||
|
||||
// HeadersContentSecurityPolicy are the content security headers added to the service's handlers
|
||||
|
|
107
internal/sessions/idptokens/api.go
Normal file
107
internal/sessions/idptokens/api.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
package idptokens
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
)
|
||||
|
||||
// endpoints
|
||||
const (
|
||||
VerifyAccessTokenEndpoint = "verify-access-token"
|
||||
VerifyIdentityTokenEndpoint = "verify-identity-token"
|
||||
)
|
||||
|
||||
// A VerifyTokenResponse is the response to verifying an access token or identity token.
|
||||
type VerifyTokenResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Claims map[string]any `json:"claims,omitempty"`
|
||||
}
|
||||
|
||||
// VerifyAccessTokenRequest is the data for verifying an access token.
|
||||
type VerifyAccessTokenRequest struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
IdentityProviderID string `json:"identityProviderId,omitempty"`
|
||||
}
|
||||
|
||||
// VerifyIdentityTokenRequest is the data for verifying an identity token.
|
||||
type VerifyIdentityTokenRequest struct {
|
||||
IdentityToken string `json:"identityToken"`
|
||||
IdentityProviderID string `json:"identityProviderId,omitempty"`
|
||||
}
|
||||
|
||||
func apiVerifyAccessToken(
|
||||
ctx context.Context,
|
||||
authenticateServiceURL string,
|
||||
request *VerifyAccessTokenRequest,
|
||||
) (*VerifyTokenResponse, error) {
|
||||
var response VerifyTokenResponse
|
||||
err := api(ctx, authenticateServiceURL, "verify-access-token", request, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func apiVerifyIdentityToken(
|
||||
ctx context.Context,
|
||||
authenticateServiceURL string,
|
||||
request *VerifyIdentityTokenRequest,
|
||||
) (*VerifyTokenResponse, error) {
|
||||
var response VerifyTokenResponse
|
||||
err := api(ctx, authenticateServiceURL, "verify-identity-token", request, &response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
func api(
|
||||
ctx context.Context,
|
||||
authenticateServiceURL string,
|
||||
endpoint string,
|
||||
request, response any,
|
||||
) error {
|
||||
u, err := urlutil.ParseAndValidateURL(authenticateServiceURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid authenticate service url: %w", err)
|
||||
}
|
||||
u = u.ResolveReference(&url.URL{
|
||||
Path: "/.pomerium/" + endpoint,
|
||||
})
|
||||
|
||||
body, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling %s http request: %w", endpoint, err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, u.String(), bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating %s http request: %w", endpoint, err)
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error executing %s http request: %w", endpoint, err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err = io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s http response: %w", endpoint, err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s http response (body=%s): %w", endpoint, body, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
162
internal/sessions/idptokens/idptokens.go
Normal file
162
internal/sessions/idptokens/idptokens.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
package idptokens
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
"github.com/pomerium/pomerium/internal/sessions"
|
||||
"github.com/pomerium/pomerium/internal/urlutil"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/identity"
|
||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||
)
|
||||
|
||||
var (
|
||||
accessTokenUUIDNamespace = uuid.MustParse("0194f6f8-e760-76a0-8917-e28ac927a34d")
|
||||
identityTokenUUIDNamespace = uuid.MustParse("0194f6f9-aec0-704e-bb4a-51054f17ad17")
|
||||
)
|
||||
|
||||
// A Loader loads sessions from IdP access and identity tokens.
|
||||
type Loader struct {
|
||||
options *config.Options
|
||||
dataBrokerServiceClient databroker.DataBrokerServiceClient
|
||||
}
|
||||
|
||||
// NewLoader creates a new Loader.
|
||||
func NewLoader(options *config.Options, dataBrokerServiceClient databroker.DataBrokerServiceClient) *Loader {
|
||||
return &Loader{
|
||||
options: options,
|
||||
dataBrokerServiceClient: dataBrokerServiceClient,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadSession loads sessions from IdP access and identity tokens.
|
||||
func (l *Loader) LoadSession(r *http.Request) (*session.Session, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
idp, err := l.options.GetIdentityProviderForRequestURL(urlutil.GetAbsoluteURL(r).String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v := r.Header.Get(httputil.HeaderPomeriumIDPAccessToken); v != "" {
|
||||
return l.loadSessionFromAccessToken(ctx, idp, v)
|
||||
} else if v := r.Header.Get(httputil.HeaderAuthorization); v != "" {
|
||||
prefix := httputil.AuthorizationTypePomeriumIDPAccessToken + " "
|
||||
if strings.HasPrefix(strings.ToLower(v), strings.ToLower(prefix)) {
|
||||
return l.loadSessionFromAccessToken(ctx, idp, v[len(prefix):])
|
||||
}
|
||||
|
||||
prefix = "Bearer " + httputil.AuthorizationTypePomeriumIDPAccessToken + "-"
|
||||
if strings.HasPrefix(strings.ToLower(v), strings.ToLower(prefix)) {
|
||||
return l.loadSessionFromAccessToken(ctx, idp, v[len(prefix):])
|
||||
}
|
||||
}
|
||||
|
||||
if v := r.Header.Get(httputil.HeaderPomeriumIDPIdentityToken); v != "" {
|
||||
return l.loadSessionFromIdentityToken(ctx, idp, v)
|
||||
} else if v := r.Header.Get(httputil.HeaderAuthorization); v != "" {
|
||||
prefix := httputil.AuthorizationTypePomeriumIDPIdentityToken + " "
|
||||
if strings.HasPrefix(strings.ToLower(v), strings.ToLower(prefix)) {
|
||||
return l.loadSessionFromIdentityToken(ctx, idp, v[len(prefix):])
|
||||
}
|
||||
|
||||
prefix = "Bearer " + httputil.AuthorizationTypePomeriumIDPIdentityToken + "-"
|
||||
if strings.HasPrefix(strings.ToLower(v), strings.ToLower(prefix)) {
|
||||
return l.loadSessionFromIdentityToken(ctx, idp, v[len(prefix):])
|
||||
}
|
||||
}
|
||||
|
||||
return nil, sessions.ErrNoSessionFound
|
||||
}
|
||||
|
||||
func (l *Loader) loadSessionFromAccessToken(ctx context.Context, idp *identity.Provider, rawAccessToken string) (*session.Session, error) {
|
||||
sessionID := uuid.NewSHA1(accessTokenUUIDNamespace, []byte(rawAccessToken)).String()
|
||||
s, err := session.Get(ctx, l.dataBrokerServiceClient, sessionID)
|
||||
if err == nil {
|
||||
return s, nil
|
||||
} else if status.Code(err) != codes.NotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := apiVerifyAccessToken(ctx, idp.GetAuthenticateServiceUrl(), &VerifyAccessTokenRequest{
|
||||
AccessToken: rawAccessToken,
|
||||
IdentityProviderID: idp.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !res.Valid {
|
||||
return nil, fmt.Errorf("invalid access token: %s", res.Error)
|
||||
}
|
||||
|
||||
// no session yet, create one
|
||||
s = newSession(sessionID, res.Claims)
|
||||
s.OauthToken = &session.OAuthToken{
|
||||
TokenType: "Bearer",
|
||||
AccessToken: rawAccessToken,
|
||||
ExpiresAt: s.ExpiresAt,
|
||||
}
|
||||
_, err = session.Put(ctx, l.dataBrokerServiceClient, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (l *Loader) loadSessionFromIdentityToken(ctx context.Context, idp *identity.Provider, rawIdentityToken string) (*session.Session, error) {
|
||||
sessionID := uuid.NewSHA1(identityTokenUUIDNamespace, []byte(rawIdentityToken)).String()
|
||||
s, err := session.Get(ctx, l.dataBrokerServiceClient, sessionID)
|
||||
if err == nil {
|
||||
return s, nil
|
||||
} else if status.Code(err) != codes.NotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := apiVerifyIdentityToken(ctx, idp.GetAuthenticateServiceUrl(), &VerifyIdentityTokenRequest{
|
||||
IdentityToken: rawIdentityToken,
|
||||
IdentityProviderID: idp.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !res.Valid {
|
||||
return nil, fmt.Errorf("invalid access token: %s", res.Error)
|
||||
}
|
||||
|
||||
// no session yet, create one
|
||||
s = newSession(sessionID, nil)
|
||||
s.IdToken = &session.IDToken{
|
||||
Subject: s.UserId,
|
||||
ExpiresAt: s.ExpiresAt,
|
||||
IssuedAt: s.IssuedAt,
|
||||
Raw: rawIdentityToken,
|
||||
}
|
||||
_, err = session.Put(ctx, l.dataBrokerServiceClient, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func newSession(sessionID string, claims map[string]any) *session.Session {
|
||||
s := &session.Session{
|
||||
Id: sessionID,
|
||||
}
|
||||
|
||||
if v, ok := claims["oid"].(string); ok {
|
||||
s.UserId = v
|
||||
} else if v, ok := claims["sub"].(string); ok {
|
||||
s.UserId = v
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
|
@ -2,6 +2,7 @@ package identity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -55,3 +56,11 @@ func (mp MockProvider) SignOut(_ http.ResponseWriter, _ *http.Request, _, _, _ s
|
|||
func (mp MockProvider) SignIn(_ http.ResponseWriter, _ *http.Request, _ string) error {
|
||||
return mp.SignInError
|
||||
}
|
||||
|
||||
func (mp MockProvider) VerifyAccessToken(_ context.Context, _ string) (map[string]any, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
||||
func (mp MockProvider) VerifyIdentityToken(_ context.Context, _ string) (map[string]any, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
|
|
|
@ -182,3 +182,11 @@ func (p *Provider) SignIn(w http.ResponseWriter, r *http.Request, state string)
|
|||
func (p *Provider) SignOut(_ http.ResponseWriter, _ *http.Request, _, _, _ string) error {
|
||||
return oidc.ErrSignoutNotImplemented
|
||||
}
|
||||
|
||||
func (p *Provider) VerifyAccessToken(ctx context.Context, rawAccessToken string) (claims map[string]any, err error) {
|
||||
return nil, fmt.Errorf("VerifyAccessToken not implemented")
|
||||
}
|
||||
|
||||
func (p *Provider) VerifyIdentityToken(ctx context.Context, rawIdentityToken string) (claims map[string]any, err error) {
|
||||
return nil, fmt.Errorf("VerifyIdentityToken not implemented")
|
||||
}
|
||||
|
|
|
@ -256,3 +256,11 @@ func (p *Provider) SignIn(w http.ResponseWriter, r *http.Request, state string)
|
|||
func (p *Provider) SignOut(_ http.ResponseWriter, _ *http.Request, _, _, _ string) error {
|
||||
return oidc.ErrSignoutNotImplemented
|
||||
}
|
||||
|
||||
func (p *Provider) VerifyAccessToken(ctx context.Context, rawAccessToken string) (claims map[string]any, err error) {
|
||||
return nil, fmt.Errorf("VerifyAccessToken not implemented")
|
||||
}
|
||||
|
||||
func (p *Provider) VerifyIdentityToken(ctx context.Context, rawIdentityToken string) (claims map[string]any, err error) {
|
||||
return nil, fmt.Errorf("VerifyIdentityToken not implemented")
|
||||
}
|
||||
|
|
|
@ -360,3 +360,48 @@ func (p *Provider) SignOut(w http.ResponseWriter, r *http.Request, idTokenHint,
|
|||
httputil.Redirect(w, r, endSessionURL.String(), http.StatusFound)
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyAccessToken verifies a raw access token using the oidc UserInfo endpoint.
|
||||
func (p *Provider) VerifyAccessToken(ctx context.Context, rawAccessToken string) (claims map[string]any, err error) {
|
||||
pp, err := p.GetProvider()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userInfo, err := pp.UserInfo(ctx, oauth2.StaticTokenSource(&oauth2.Token{
|
||||
AccessToken: rawAccessToken,
|
||||
TokenType: "Bearer",
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims = map[string]any{}
|
||||
err = userInfo.Claims(claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
// VerifyIdentityToken verifies a raw identity token using the oidc ID Token Verifier.
|
||||
func (p *Provider) VerifyIdentityToken(ctx context.Context, rawIdentityToken string) (claims map[string]any, err error) {
|
||||
verifier, err := p.GetVerifier()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token, err := verifier.Verify(ctx, rawIdentityToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims = map[string]any{}
|
||||
err = token.Claims(claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import (
|
|||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/identity/identity"
|
||||
"github.com/pomerium/pomerium/pkg/identity/oauth"
|
||||
"github.com/pomerium/pomerium/pkg/identity/oauth/apple"
|
||||
|
@ -23,7 +25,6 @@ import (
|
|||
"github.com/pomerium/pomerium/pkg/identity/oidc/okta"
|
||||
"github.com/pomerium/pomerium/pkg/identity/oidc/onelogin"
|
||||
"github.com/pomerium/pomerium/pkg/identity/oidc/ping"
|
||||
oteltrace "go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// State is the identity state.
|
||||
|
@ -36,6 +37,8 @@ type Authenticator interface {
|
|||
Revoke(context.Context, *oauth2.Token) error
|
||||
Name() string
|
||||
UpdateUserInfo(ctx context.Context, t *oauth2.Token, v any) error
|
||||
VerifyAccessToken(ctx context.Context, rawAccessToken string) (claims map[string]any, err error)
|
||||
VerifyIdentityToken(ctx context.Context, rawIdentityToken string) (claims map[string]any, err error)
|
||||
|
||||
SignIn(w http.ResponseWriter, r *http.Request, state string) error
|
||||
SignOut(w http.ResponseWriter, r *http.Request, idTokenHint, authenticateSignedOutURL, redirectToURL string) error
|
||||
|
|
Loading…
Add table
Reference in a new issue