chore(deps): bump github.com/libregraph/lico from 0.62.0 to 0.64.0

Bumps [github.com/libregraph/lico](https://github.com/libregraph/lico) from 0.62.0 to 0.64.0.
- [Changelog](https://github.com/libregraph/lico/blob/master/CHANGELOG.md)
- [Commits](https://github.com/libregraph/lico/compare/v0.62.0...v0.64.0)

---
updated-dependencies:
- dependency-name: github.com/libregraph/lico
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2024-09-30 07:06:56 +00:00
committed by Ralf Haferkamp
parent fa80fbed9a
commit 114b519416
18 changed files with 442 additions and 127 deletions

4
go.mod
View File

@@ -58,7 +58,7 @@ require (
github.com/kovidgoyal/imaging v1.6.3
github.com/leonelquinteros/gotext v1.6.1
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.62.0
github.com/libregraph/lico v0.64.0
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
@@ -78,7 +78,7 @@ require (
github.com/r3labs/sse/v2 v2.10.0
github.com/riandyrn/otelchi v0.10.0
github.com/rogpeppe/go-internal v1.13.1
github.com/rs/cors v1.11.0
github.com/rs/cors v1.11.1
github.com/rs/zerolog v1.33.0
github.com/shamaton/msgpack/v2 v2.2.2
github.com/sirupsen/logrus v1.9.3

8
go.sum
View File

@@ -782,8 +782,8 @@ github.com/leonelquinteros/gotext v1.6.1 h1:PuTN8YUqHvfPZxW+fPXp7o0Fc2zN9L2wXBZr
github.com/leonelquinteros/gotext v1.6.1/go.mod h1:qQRISjoonXYFdRGrTG1LARQ38Gpibad0IPeB4hpvyyM=
github.com/libregraph/idm v0.5.0 h1:tDMwKbAOZzdeDYMxVlY5PbSqRKO7dbAW9KT42A51WSk=
github.com/libregraph/idm v0.5.0/go.mod h1:BGMwIQ/6orJSPVzJ1x6kgG2JyG9GY05YFmbsnaD80k0=
github.com/libregraph/lico v0.62.0 h1:4aa0hp8kLCj/oy/aGuGKQCzdQRLHY/cvDs25bdkdcXU=
github.com/libregraph/lico v0.62.0/go.mod h1:Byuq0dALiJD9hpx2gvX5UTVZ6eWZs3sO0SeoXQQeFcE=
github.com/libregraph/lico v0.64.0 h1:fbMV2ALjrOysGL0m58bhRrF+9e/HCL5RkoSwMN+xoWQ=
github.com/libregraph/lico v0.64.0/go.mod h1:J2ZNe1DcO+K/5ptOOrQk2A2mn6OwXRdGUI4ASgw2WGg=
github.com/libregraph/oidc-go v1.1.0 h1:RyudjL3UyQblqeBQI06W53PniWobqODeeyAy6v/HumA=
github.com/libregraph/oidc-go v1.1.0/go.mod h1:qW9ubcXvZrfbbWZBaLMuk7bt5qAUMYyt9/NtXQt07Cw=
github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE=
@@ -1052,8 +1052,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po=
github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=

View File

@@ -4,6 +4,30 @@
## v0.64.0 (2024-09-18)
- Implement refresh and revoke for lg identifier backend session
- Pass real src ip and user agent to lg identifier backend
- Fix variable shadowing making error checks ineffective
## v0.63.0 (2024-09-10)
- Bump semver from 5.7.1 to 5.7.2 in /identifier
- Ignore js license ranger border check warnings
- Fix js license ranger for new source-map-explorer
- Bump source-map-explorer to 2.5.3 in /identifier
- Update linter CI version
- Fix access token sid claim when provided via lg backend
- Bump google.golang.org/protobuf from 1.30.0 to 1.33.0
- Bump github.com/rs/cors from 1.10.1 to 1.11.1
- Add password visibility icon in login dialog
- Bump github.com/spf13/cobra from 1.7.0 to 1.8.1
- Remove :443 from Host header for secure referrer/origin check
- Allow authorize requests wihout openid scope
- Bump github.com/gorilla/schema from 1.2.0 to 1.4.1
## v0.62.0 (2024-05-08)
- Update golangci-lint config

View File

@@ -12,13 +12,27 @@
tls self_signed
# konnect oidc
proxy /.well-known/openid-configuration 127.0.0.1:8777
proxy /konnect/v1/jwks.json 127.0.0.1:8777
proxy /konnect/v1/token 127.0.0.1:8777
proxy /konnect/v1/userinfo 127.0.0.1:8777
proxy /konnect/v1/static 127.0.0.1:8777
proxy /konnect/v1/session 127.0.0.1:8777
proxy /konnect/v1/register 127.0.0.1:8777
proxy /.well-known/openid-configuration 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/jwks.json 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/token 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/userinfo 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/static 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/session 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/register 127.0.0.1:8777 {
transparent
}
# konnect identifier development via webpack-dev-server
proxy /signin/v1/ 127.0.0.1:3001 {
@@ -46,6 +60,7 @@
#proxy /provider/simple/konnect/v1/authorize 127.0.0.1:8777 {
# without /provider/simple
# header_upstream X-Forwarded-Prefix /provider/simple
# transparent
#}
# konnect cookieserver, start with python3 ./examples/cookieserver.py 8088

View File

@@ -9,13 +9,27 @@
tls self_signed
# konnect oidc
proxy /.well-known/openid-configuration 127.0.0.1:8777
proxy /konnect/v1/jwks.json 127.0.0.1:8777
proxy /konnect/v1/token 127.0.0.1:8777
proxy /konnect/v1/userinfo 127.0.0.1:8777
proxy /konnect/v1/static 127.0.0.1:8777
proxy /konnect/v1/session 127.0.0.1:8777
proxy /konnect/v1/register 127.0.0.1:8777
proxy /.well-known/openid-configuration 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/jwks.json 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/token 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/userinfo 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/static 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/session 127.0.0.1:8777 {
transparent
}
proxy /konnect/v1/register 127.0.0.1:8777 {
transparent
}
# konnect identifier login area
proxy /signin/ 127.0.0.1:8777 {

View File

@@ -19,6 +19,7 @@ package lico
import (
"context"
"net/http"
"github.com/golang-jwt/jwt/v4"
)
@@ -30,7 +31,10 @@ type key int
// claimsKey is the key for claims in contexts. It is
// unexported; clients use konnect.NewClaimsContext and
// connect.FromClaimsContext instead of using this key directly.
var claimsKey key
const (
claimsKey key = iota
requestKey
)
// NewClaimsContext returns a new Context that carries value auth.
func NewClaimsContext(ctx context.Context, claims jwt.Claims) context.Context {
@@ -42,3 +46,14 @@ func FromClaimsContext(ctx context.Context) (jwt.Claims, bool) {
claims, ok := ctx.Value(claimsKey).(jwt.Claims)
return claims, ok
}
// NewRequestContext returns a new Context that carries a request object.
func NewRequestContext(ctx context.Context, req *http.Request) context.Context {
return context.WithValue(ctx, requestKey, req)
}
// FromRequestContext returns the Request object stored in ctx, if any.
func FromRequestContext(ctx context.Context) (*http.Request, bool) {
req, ok := ctx.Value(requestKey).(*http.Request)
return req, ok
}

View File

@@ -54,8 +54,9 @@ const (
)
const (
apiPathMe = "/api/v1/me"
apiPathUsers = "/api/v1/users"
apiPathMe = "/api/v1/me"
apiPathUsers = "/api/v1/users"
apiPathRevokeSignInSession = "/api/v1/me/revokeSignInSession"
)
var libreGraphSpportedScopes = []string{
@@ -66,6 +67,8 @@ var libreGraphSpportedScopes = []string{
}
type LibreGraphIdentifierBackend struct {
config *config.Config
supportedScopes []string
logger logrus.FieldLogger
@@ -245,7 +248,7 @@ func (u *libreGraphUser) setRequiredScopes(selectedScope string, scopeMap *order
}
func (u *libreGraphUser) sessionID() string {
if accessTokenClaims, ok := u.identityClaims[""].(map[string]interface{}); ok {
if accessTokenClaims, ok := u.identityClaims[konnect.InternalExtraAccessTokenClaimsClaim].(map[string]interface{}); ok {
if sessionID, withSessionID := accessTokenClaims[oidc.SessionIDClaim].(string); withSessionID {
if sessionID != "" {
return sessionID
@@ -297,6 +300,8 @@ func NewLibreGraphIdentifierBackend(
transport.IdleConnTimeout = 30 * time.Second
b := &LibreGraphIdentifierBackend{
config: c,
supportedScopes: supportedScopes,
logger: c.Logger,
@@ -348,16 +353,27 @@ func (b *LibreGraphIdentifierBackend) Logon(ctx context.Context, audience, usern
}
req.SetBasicAuth(username, password)
if record == nil {
record, _ = identifier.RecordFromRequestContext(ctx, b.config)
}
if record != nil {
// Inject HTTP headers.
if record.HelloRequest.Flow != "" {
req.Header.Set("X-Flow", record.HelloRequest.Flow)
if record.HelloRequest != nil {
if record.HelloRequest.Flow != "" {
req.Header.Set("X-Flow", record.HelloRequest.Flow)
}
if record.HelloRequest.RawScope != "" {
req.Header.Set("X-Scope", record.HelloRequest.RawScope)
}
if record.HelloRequest.RawPrompt != "" {
req.Header.Set("X-Prompt", record.HelloRequest.RawPrompt)
}
}
if record.HelloRequest.RawScope != "" {
req.Header.Set("X-Scope", record.HelloRequest.RawScope)
if record.RealIP != "" {
req.Header.Set("X-User-Real-IP", record.RealIP)
}
if record.HelloRequest.RawPrompt != "" {
req.Header.Set("X-Prompt", record.HelloRequest.RawPrompt)
if record.UserAgent != "" {
req.Header.Set("X-User-Real-User-Agent", record.UserAgent)
}
}
req.Header.Set("User-Agent", utils.DefaultHTTPUserAgent)
@@ -407,7 +423,7 @@ func (b *LibreGraphIdentifierBackend) Logon(ctx context.Context, audience, usern
// Put the user into the record (if any).
if record != nil {
record.UserFromBackend = user
record.BackendUser = user
}
return true, &userID, &sessionID, user, nil
@@ -419,8 +435,8 @@ func (b *LibreGraphIdentifierBackend) Logon(ctx context.Context, audience, usern
func (b *LibreGraphIdentifierBackend) GetUser(ctx context.Context, entryID string, sessionRef *string, requestedScopes map[string]bool) (backends.UserFromBackend, error) {
record, _ := identifier.FromRecordContext(ctx)
if record != nil {
if record.UserFromBackend != nil {
if user, ok := record.UserFromBackend.(*libreGraphUser); ok {
if record.BackendUser != nil {
if user, ok := record.BackendUser.(*libreGraphUser); ok {
// Fastpath, if logon previously injected the user.
if user.ID == entryID {
return user, nil
@@ -455,6 +471,23 @@ func (b *LibreGraphIdentifierBackend) GetUser(ctx context.Context, entryID strin
req.Header.Set("X-SessionID", sessionID)
}
}
if record == nil {
record, _ = identifier.RecordFromRequestContext(ctx, b.config)
}
if record != nil {
if record.IdentifiedUser != nil {
if ok, ts := record.IdentifiedUser.LoggedOn(); ok {
req.Header.Set("X-User-Logon-At", ts.UTC().Format(http.TimeFormat))
}
}
if record.RealIP != "" {
req.Header.Set("X-User-Real-IP", record.RealIP)
}
if record.UserAgent != "" {
req.Header.Set("X-User-Real-User-Agent", record.UserAgent)
}
}
req.Header.Set("User-Agent", utils.DefaultHTTPUserAgent)
// Inject select parameter.
@@ -489,7 +522,7 @@ func (b *LibreGraphIdentifierBackend) GetUser(ctx context.Context, entryID strin
return user, nil
}
// ResolveUserByUsername implements the Beckend interface, providing lookup for
// ResolveUserByUsername implements the Backend interface, providing lookup for
// user by providing the username. Requests are bound to the provided context.
func (b *LibreGraphIdentifierBackend) ResolveUserByUsername(ctx context.Context, username string) (backends.UserFromBackend, error) {
// Libregraph backend accept both user name and ID lookups, so this is
@@ -499,11 +532,72 @@ func (b *LibreGraphIdentifierBackend) ResolveUserByUsername(ctx context.Context,
// RefreshSession implements the Backend interface.
func (b *LibreGraphIdentifierBackend) RefreshSession(ctx context.Context, userID string, sessionRef *string, claims map[string]interface{}) error {
user, err := b.GetUser(ctx, userID, sessionRef, nil)
if err != nil {
return err
}
if user == nil {
return fmt.Errorf("refresh session did not yield a user")
}
return nil
}
// DestroySession implements the Backend interface providing destroy to KC session.
// DestroySession implements the Backend interface providing explicit revoke
// of the backend session.
func (b *LibreGraphIdentifierBackend) DestroySession(ctx context.Context, sessionRef *string) error {
var requestedScopes map[string]bool
record, _ := identifier.FromRecordContext(ctx)
if record != nil {
if record.HelloRequest != nil {
requestedScopes = record.HelloRequest.Scopes
}
}
_, revokeSessionURL := b.getRevokeSigninSessionURL(requestedScopes)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, revokeSessionURL, http.NoBody)
if err != nil {
return fmt.Errorf("libregraph identifier backend destroy session request error: %w", err)
}
if sessionRef != nil {
sessionID := *sessionRef
if !strings.HasPrefix(sessionID, libreGraphIdentifierBackendName+":") {
// Only send the session ID if it is not a ref generated by lico.
req.Header.Set("X-SessionID", sessionID)
}
}
if record == nil {
record, _ = identifier.RecordFromRequestContext(ctx, b.config)
}
if record != nil {
// Inject HTTP headers.
if record.RealIP != "" {
req.Header.Set("X-User-Real-IP", record.RealIP)
}
if record.UserAgent != "" {
req.Header.Set("X-User-Real-User-Agent", record.UserAgent)
}
}
req.Header.Set("User-Agent", utils.DefaultHTTPUserAgent)
response, err := b.client.Do(req)
if err != nil {
return fmt.Errorf("libregraph identifier backend destroy session request failed: %w", err)
}
defer response.Body.Close()
switch response.StatusCode {
case http.StatusOK:
// breaks
case http.StatusNotFound:
return nil
case http.StatusUnauthorized:
return nil
default:
return fmt.Errorf("libregraph identifier backend logon request unexpected response status: %d", response.StatusCode)
}
return nil
}
@@ -558,3 +652,9 @@ func (b *LibreGraphIdentifierBackend) getUserURL(requestedScopes map[string]bool
return scope, baseURL + apiPathUsers
}
func (b *LibreGraphIdentifierBackend) getRevokeSigninSessionURL(requestedScopes map[string]bool) (string, string) {
scope, baseURL := b.getBaseURL(requestedScopes)
return scope, baseURL + apiPathRevokeSignInSession
}

View File

@@ -19,26 +19,56 @@ package identifier
import (
"context"
"net"
"net/http"
konnect "github.com/libregraph/lico"
"github.com/libregraph/lico/config"
"github.com/libregraph/lico/identifier/backends"
"github.com/libregraph/lico/utils"
)
// Record is the struct which the identifier puts into the context.
type Record struct {
HelloRequest *HelloRequest
UserFromBackend backends.UserFromBackend
HelloRequest *HelloRequest
RealIP string
UserAgent string
BackendUser backends.UserFromBackend
IdentifiedUser *IdentifiedUser
}
func NewRecord(req *http.Request, c *config.Config) *Record {
record := &Record{
UserAgent: req.UserAgent(),
}
trusted, _ := utils.IsRequestFromTrustedSource(req, c.TrustedProxyIPs, c.TrustedProxyNets)
if trusted {
record.RealIP = req.Header.Get("X-Real-Ip")
}
if record.RealIP == "" {
if ip, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
record.RealIP = ip
}
}
return record
}
// key is an unexported type for keys defined in this package.
// This prevents collisions with keys defined in other packages.
type key int
// recordKey is the key for identifier.Record in Contexts. It is
// unexported; clients use identifier.NewContext and identifier.FromContext
// instead of using this key directly.
var recordKey key
// Keys for context data.
// Unexported; Clients use identifier.New{?}Context and
// identifier.From{?}Context functions instead of using these keys directly.
const (
recordKey key = iota
)
// NewRecordContext returns a new Context that carries value HelloRequest.
// NewRecordContext returns a new Context that carries the Record.
func NewRecordContext(ctx context.Context, record *Record) context.Context {
return context.WithValue(ctx, recordKey, record)
}
@@ -48,3 +78,12 @@ func FromRecordContext(ctx context.Context) (*Record, bool) {
record, ok := ctx.Value(recordKey).(*Record)
return record, ok
}
// RecordFromRequestContext returns a new Record value based on the request
// stored in ctx, if any.
func RecordFromRequestContext(ctx context.Context, c *config.Config) (*Record, bool) {
if req, ok := konnect.FromRequestContext(ctx); ok {
return NewRecord(req, c), true
}
return nil, false
}

View File

@@ -21,6 +21,7 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"net"
"net/http"
"net/url"
"strings"
@@ -28,6 +29,7 @@ import (
"github.com/sirupsen/logrus"
konnect "github.com/libregraph/lico"
"github.com/libregraph/lico/identity/authorities"
"github.com/libregraph/lico/utils"
)
@@ -57,6 +59,14 @@ func (i *Identifier) secureHandler(handler http.Handler) http.Handler {
// NOTE: this does not protect from DNS rebinding. Protection for that
// should be added at the frontend proxy.
requiredHost := req.Host
if host, port, splitErr := net.SplitHostPort(requiredHost); splitErr == nil {
if port == "443" {
// Ignore the port 443 as it is the default port and it is
// usually not part of any of the urls. It might be in the
// request for HTTP/3 requests.
requiredHost = host
}
}
// This follows https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
for {
@@ -172,7 +182,7 @@ func (i *Identifier) handleLogon(rw http.ResponseWriter, req *http.Request) {
addNoCacheResponseHeaders(rw.Header())
record := &Record{}
record := NewRecord(req, i.Config.Config)
if r.Hello != nil {
err = r.Hello.parse()
@@ -184,7 +194,7 @@ func (i *Identifier) handleLogon(rw http.ResponseWriter, req *http.Request) {
record.HelloRequest = r.Hello
}
req = req.WithContext(NewRecordContext(req.Context(), record))
req = req.WithContext(NewRecordContext(konnect.NewRequestContext(req.Context(), req), record))
// Params is an array like this [$username, $password, $mode], defining a
// extensible way to extend login modes over time. The minimal length of

View File

@@ -33,7 +33,7 @@
"serve": "vite preview",
"test": "vitest",
"lint": "eslint --max-warnings=0 src/**/*.{ts,tsx,js,jsx}",
"licenses": "NODE_PATH=./node_modules node ../scripts/js-license-ranger.js",
"licenses": "node ../scripts/js-license-ranger.mjs",
"analyze": "source-map-explorer 'build/static/assets/*.js'"
},
"devDependencies": {
@@ -59,7 +59,7 @@
"i18next-parser": "^5.4.0",
"if-node-version": "^1.1.1",
"jsdom": "^22.1.0",
"source-map-explorer": "^1.8.0",
"source-map-explorer": "^2.5.3",
"typescript": "^5.2.2",
"vite": "^4.5.2",
"vite-plugin-checker": "^0.6.2",

View File

@@ -28,7 +28,9 @@ type key int
// authRecordKey is the key for identity.AuthRecord in Contexts. It is
// unexported; clients use identity.NewContext and identity.FromContext
// instead of using this key directly.
var authRecordKey key
const (
authRecordKey key = iota
)
// NewContext returns a new Context that carries value auth.
func NewContext(ctx context.Context, auth AuthRecord) context.Context {

View File

@@ -200,6 +200,10 @@ func (im *IdentifierIdentityManager) Authenticate(ctx context.Context, rw http.R
}
if user != nil {
record := identifier.NewRecord(req, im.identifier.Config.Config)
record.IdentifiedUser = user.IdentifiedUser
ctx = identifier.NewRecordContext(ctx, record)
// Inject required scopes into request.
for scope, ok := range user.RequiredScopes() {
ar.Scopes[scope] = ok

View File

@@ -265,28 +265,36 @@ func (ar *AuthenticationRequest) ApplyRequestObject(roc *RequestObjectClaims, me
// Validate validates the request data of the accociated authentication request.
func (ar *AuthenticationRequest) Validate(keyFunc jwt.Keyfunc) error {
if _, ok := ar.Scopes[oidc.ScopeOpenID]; !ok {
return ar.NewBadRequest(oidc.ErrorCodeOAuth2InvalidRequest, "missing openid scope in request")
}
switch ar.RawResponseType {
case oidc.ResponseTypeCode:
// Code flow.
// breaks
case oidc.ResponseTypeCodeIDToken:
// Hybgrid flow.
if _, ok := ar.Scopes[oidc.ScopeOpenID]; !ok {
return ar.NewBadRequest(oidc.ErrorCodeOAuth2InvalidRequest, "missing openid scope in request")
}
// breaks
case oidc.ResponseTypeCodeToken:
// Hybgrid flow.
// breaks
case oidc.ResponseTypeCodeIDTokenToken:
// Hybgrid flow.
if _, ok := ar.Scopes[oidc.ScopeOpenID]; !ok {
return ar.NewBadRequest(oidc.ErrorCodeOAuth2InvalidRequest, "missing openid scope in request")
}
// breaks
case oidc.ResponseTypeIDToken:
// Implicit flow.
if _, ok := ar.Scopes[oidc.ScopeOpenID]; !ok {
return ar.NewBadRequest(oidc.ErrorCodeOAuth2InvalidRequest, "missing openid scope in request")
}
fallthrough
case oidc.ResponseTypeIDTokenToken:
// Implicit flow with access token.
if _, ok := ar.Scopes[oidc.ScopeOpenID]; !ok {
return ar.NewBadRequest(oidc.ErrorCodeOAuth2InvalidRequest, "missing openid scope in request")
}
if ar.Nonce == "" {
return ar.NewError(oidc.ErrorCodeOAuth2InvalidRequest, "nonce is required for implicit flow")
}

View File

@@ -93,6 +93,8 @@ func (p *Provider) AuthorizeHandler(rw http.ResponseWriter, req *http.Request) {
addResponseHeaders(rw.Header())
ctx := konnect.NewRequestContext(req.Context(), req)
// OpenID Connect 1.0 authentication request validation.
// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitValidation
err = req.ParseForm()
@@ -106,7 +108,7 @@ func (p *Provider) AuthorizeHandler(rw http.ResponseWriter, req *http.Request) {
if claims, ok := token.Claims.(*payload.RequestObjectClaims); ok {
// Validate signed request tokens according to spec defined at
// https://openid.net/specs/openid-connect-core-1_0.html#SignedRequestObject
registration, _ := p.clients.Get(req.Context(), claims.ClientID)
registration, _ := p.clients.Get(ctx, claims.ClientID)
if registration != nil {
if registration.RawRequestObjectSigningAlg != "" {
if token.Method.Alg() != registration.RawRequestObjectSigningAlg {
@@ -156,7 +158,7 @@ func (p *Provider) AuthorizeHandler(rw http.ResponseWriter, req *http.Request) {
}
// Inject implicit scopes set by client registration.
if registration, _ := p.clients.Get(req.Context(), ar.ClientID); registration != nil {
if registration, _ := p.clients.Get(ctx, ar.ClientID); registration != nil {
err = registration.ApplyImplicitScopes(ar.Scopes)
if err != nil {
p.logger.WithError(err).Debugln("failed to apply implicit scopes")
@@ -171,7 +173,7 @@ func (p *Provider) AuthorizeHandler(rw http.ResponseWriter, req *http.Request) {
// Authorization Server Authenticates End-User
// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthenticates
auth, err = p.identityManager.Authenticate(req.Context(), rw, req, ar, p.guestManager)
auth, err = p.identityManager.Authenticate(ctx, rw, req, ar, p.guestManager)
if err != nil {
goto done
}
@@ -190,7 +192,7 @@ func (p *Provider) AuthorizeHandler(rw http.ResponseWriter, req *http.Request) {
// Authorization Server Obtains End-User Consent/Authorization
// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitConsent
auth, err = auth.Manager().Authorize(req.Context(), rw, req, ar, auth)
auth, err = auth.Manager().Authorize(ctx, rw, req, ar, auth)
if err != nil {
goto done
}
@@ -213,7 +215,7 @@ func (p *Provider) AuthorizeResponse(rw http.ResponseWriter, req *http.Request,
goto done
}
ctx = identity.NewContext(req.Context(), auth)
ctx = identity.NewContext(konnect.NewRequestContext(req.Context(), req), auth)
// Create session.
session, err = p.updateOrCreateSession(rw, req, ar, auth)
@@ -336,6 +338,8 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("Cache-Control", "no-store")
rw.Header().Set("Pragma", "no-cache")
ctx := konnect.NewRequestContext(req.Context(), req)
// Validate request method
switch req.Method {
case http.MethodPost:
@@ -367,7 +371,7 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
}
// Additional validations according to https://tools.ietf.org/html/rfc6749#section-4.1.3
clientDetails, err = p.clients.Lookup(req.Context(), tr.ClientID, tr.ClientSecret, tr.RedirectURI, "", false)
clientDetails, err = p.clients.Lookup(ctx, tr.ClientID, tr.ClientSecret, tr.RedirectURI, "", false)
if err != nil {
err = konnectoidc.NewOAuth2Error(oidc.ErrorCodeOAuth2AccessDenied, err.Error())
goto done
@@ -410,8 +414,8 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
}
}
if _, ok := identity.FromContext(req.Context()); !ok {
req = req.WithContext(identity.NewContext(req.Context(), auth))
if _, ok := identity.FromContext(ctx); !ok {
ctx = identity.NewContext(ctx, auth)
}
case oidc.GrantTypeRefreshToken:
@@ -437,10 +441,11 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
goto done
}
ctx := konnect.NewClaimsContext(req.Context(), claims)
ctx = konnect.NewClaimsContext(ctx, claims)
currentIdentityManager, err := p.getIdentityManagerFromClaims(claims.IdentityProvider, claims.IdentityClaims)
if err != nil {
currentIdentityManager, claimsErr := p.getIdentityManagerFromClaims(claims.IdentityProvider, claims.IdentityClaims)
if claimsErr != nil {
err = claimsErr
goto done
}
@@ -499,16 +504,16 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
}
// Create access token.
accessTokenString, err = p.makeAccessToken(req.Context(), ar.ClientID, auth, signinMethod)
accessTokenString, err = p.makeAccessToken(ctx, ar.ClientID, auth, signinMethod)
if err != nil {
goto done
}
switch tr.GrantType {
case oidc.GrantTypeAuthorizationCode:
// Create ID token when not previously requested.
if !ar.ResponseTypes[oidc.ResponseTypeIDToken] {
idTokenString, err = p.makeIDToken(req.Context(), ar, auth, session, accessTokenString, "", signinMethod)
// Create ID token when not previously requested amd openid scope is authorized.
if !ar.ResponseTypes[oidc.ResponseTypeIDToken] && authorizedScopes[oidc.ScopeOpenID] {
idTokenString, err = p.makeIDToken(ctx, ar, auth, session, accessTokenString, "", signinMethod)
if err != nil {
goto done
}
@@ -516,7 +521,7 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
// Create refresh token when granted.
if authorizedScopes[oidc.ScopeOfflineAccess] {
refreshTokenString, err = p.makeRefreshToken(req.Context(), ar.ClientID, auth, nil)
refreshTokenString, err = p.makeRefreshToken(ctx, ar.ClientID, auth, nil)
if err != nil {
goto done
}
@@ -595,7 +600,7 @@ func (p *Provider) UserInfoHandler(rw http.ResponseWriter, req *http.Request) {
userID, sessionRef := p.getUserIDAndSessionRefFromClaims(&claims.StandardClaims, claims.SessionClaims, claims.IdentityClaims)
ctx := konnect.NewClaimsContext(req.Context(), claims)
ctx := konnect.NewClaimsContext(konnect.NewRequestContext(req.Context(), req), claims)
currentIdentityManager, err := p.getIdentityManagerFromClaims(claims.IdentityProvider, claims.IdentityClaims)
if err != nil {
@@ -712,7 +717,7 @@ done:
// Support returning signed user info if the registered client requested it
// as specified in https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse and
// https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
registration, _ := p.clients.Get(req.Context(), claims.Audience)
registration, _ := p.clients.Get(ctx, claims.Audience)
if registration != nil {
if registration.RawUserInfoSignedResponseAlg != "" {
// Get alg.
@@ -720,7 +725,7 @@ done:
// Set extra claims.
responseAsMap[oidc.IssuerIdentifierClaim] = p.issuerIdentifier
responseAsMap[oidc.AudienceClaim] = registration.ID
tokenString, err := p.makeJWT(req.Context(), alg, jwt.MapClaims(responseAsMap))
tokenString, err := p.makeJWT(ctx, alg, jwt.MapClaims(responseAsMap))
if err != nil {
p.logger.WithFields(utils.ErrorAsFields(err)).Debugln("userinfo request failed to encode jwt")
p.ErrorPage(rw, http.StatusInternalServerError, "", err.Error())
@@ -749,6 +754,8 @@ func (p *Provider) EndSessionHandler(rw http.ResponseWriter, req *http.Request)
addResponseHeaders(rw.Header())
ctx := konnect.NewRequestContext(req.Context(), req)
// Validate request.
err = req.ParseForm()
if err != nil {
@@ -783,7 +790,7 @@ func (p *Provider) EndSessionHandler(rw http.ResponseWriter, req *http.Request)
}
// Authorization unauthenticates end user.
err = currentIdentityManager.EndSession(req.Context(), rw, req, esr)
err = currentIdentityManager.EndSession(ctx, rw, req, esr)
if err != nil {
goto done
}

16
vendor/github.com/rs/cors/cors.go generated vendored
View File

@@ -364,9 +364,11 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
// Note: the Fetch standard guarantees that at most one
// Access-Control-Request-Headers header is present in the preflight request;
// see step 5.2 in https://fetch.spec.whatwg.org/#cors-preflight-fetch-0.
reqHeaders, found := first(r.Header, "Access-Control-Request-Headers")
if found && !c.allowedHeadersAll && !c.allowedHeaders.Subsumes(reqHeaders[0]) {
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders[0])
// However, some gateways split that header into multiple headers of the same name;
// see https://github.com/rs/cors/issues/184.
reqHeaders, found := r.Header["Access-Control-Request-Headers"]
if found && !c.allowedHeadersAll && !c.allowedHeaders.Accepts(reqHeaders) {
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
return
}
if c.allowedOriginsAll {
@@ -391,9 +393,7 @@ func (c *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
if len(c.maxAge) > 0 {
headers["Access-Control-Max-Age"] = c.maxAge
}
if c.Log != nil {
c.logf(" Preflight response headers: %v", headers)
}
c.logf(" Preflight response headers: %v", headers)
}
// handleActualRequest handles simple cross-origin requests, actual request or redirects
@@ -440,9 +440,7 @@ func (c *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
if c.allowCredentials {
headers["Access-Control-Allow-Credentials"] = headerTrue
}
if c.Log != nil {
c.logf(" Actual response added headers: %v", headers)
}
c.logf(" Actual response added headers: %v", headers)
}
// convenience method. checks if a logger is set.

View File

@@ -52,48 +52,136 @@ func (set SortedSet) String() string {
return strings.Join(elems, ",")
}
// Subsumes reports whether csv is a sequence of comma-separated names that are
// - all elements of set,
// - sorted in lexicographically order,
// Accepts reports whether values is a sequence of list-based field values
// whose elements are
// - all members of set,
// - sorted in lexicographical order,
// - unique.
func (set SortedSet) Subsumes(csv string) bool {
if csv == "" {
return true
}
posOfLastNameSeen := -1
chunkSize := set.maxLen + 1 // (to accommodate for at least one comma)
for {
// As a defense against maliciously long names in csv,
// we only process at most chunkSize bytes per iteration.
end := min(len(csv), chunkSize)
comma := strings.IndexByte(csv[:end], ',')
var name string
if comma == -1 {
name = csv
} else {
name = csv[:comma]
func (set SortedSet) Accepts(values []string) bool {
var ( // effectively constant
maxLen = maxOWSBytes + set.maxLen + maxOWSBytes + 1 // +1 for comma
)
var (
posOfLastNameSeen = -1
name string
commaFound bool
emptyElements int
ok bool
)
for _, s := range values {
for {
// As a defense against maliciously long names in s,
// we process only a small number of s's leading bytes per iteration.
name, s, commaFound = cutAtComma(s, maxLen)
name, ok = trimOWS(name, maxOWSBytes)
if !ok {
return false
}
if name == "" {
// RFC 9110 requires recipients to tolerate
// "a reasonable number of empty list elements"; see
// https://httpwg.org/specs/rfc9110.html#abnf.extension.recipient.
emptyElements++
if emptyElements > maxEmptyElements {
return false
}
if !commaFound { // We have now exhausted the names in s.
break
}
continue
}
pos, ok := set.m[name]
if !ok {
return false
}
// The names in s are expected to be sorted in lexicographical order
// and to each appear at most once.
// Therefore, the positions (in set) of the names that
// appear in s should form a strictly increasing sequence.
// If that's not actually the case, bail out.
if pos <= posOfLastNameSeen {
return false
}
posOfLastNameSeen = pos
if !commaFound { // We have now exhausted the names in s.
break
}
}
pos, found := set.m[name]
if !found {
return false
}
// The names in csv are expected to be sorted in lexicographical order
// and appear at most once in csv.
// Therefore, the positions (in set) of the names that
// appear in csv should form a strictly increasing sequence.
// If that's not actually the case, bail out.
if pos <= posOfLastNameSeen {
return false
}
posOfLastNameSeen = pos
if comma < 0 { // We've now processed all the names in csv.
break
}
csv = csv[comma+1:]
}
return true
}
const (
maxOWSBytes = 1 // number of leading/trailing OWS bytes tolerated
maxEmptyElements = 16 // number of empty list elements tolerated
)
func cutAtComma(s string, n int) (before, after string, found bool) {
// Note: this implementation draws inspiration from strings.Cut's.
end := min(len(s), n)
if i := strings.IndexByte(s[:end], ','); i >= 0 {
after = s[i+1:] // deal with this first to save one bounds check
return s[:i], after, true
}
return s, "", false
}
// TrimOWS trims up to n bytes of [optional whitespace (OWS)]
// from the start of and/or the end of s.
// If no more than n bytes of OWS are found at the start of s
// and no more than n bytes of OWS are found at the end of s,
// it returns the trimmed result and true.
// Otherwise, it returns the original string and false.
//
// [optional whitespace (OWS)]: https://httpwg.org/specs/rfc9110.html#whitespace
func trimOWS(s string, n int) (trimmed string, ok bool) {
if s == "" {
return s, true
}
trimmed, ok = trimRightOWS(s, n)
if !ok {
return s, false
}
trimmed, ok = trimLeftOWS(trimmed, n)
if !ok {
return s, false
}
return trimmed, true
}
func trimLeftOWS(s string, n int) (string, bool) {
sCopy := s
var i int
for len(s) > 0 {
if i > n {
return sCopy, false
}
if !(s[0] == ' ' || s[0] == '\t') {
break
}
s = s[1:]
i++
}
return s, true
}
func trimRightOWS(s string, n int) (string, bool) {
sCopy := s
var i int
for len(s) > 0 {
if i > n {
return sCopy, false
}
last := len(s) - 1
if !(s[last] == ' ' || s[last] == '\t') {
break
}
s = s[:last]
i++
}
return s, true
}
// TODO: when updating go directive to 1.21 or later,
// use min builtin instead.
func min(a, b int) int {

9
vendor/github.com/rs/cors/utils.go generated vendored
View File

@@ -1,7 +1,6 @@
package cors
import (
"net/http"
"strings"
)
@@ -24,11 +23,3 @@ func convert(s []string, f func(string) string) []string {
}
return out
}
func first(hdrs http.Header, k string) ([]string, bool) {
v, found := hdrs[k]
if !found || len(v) == 0 {
return nil, false
}
return v[:1], true
}

4
vendor/modules.txt vendored
View File

@@ -1300,7 +1300,7 @@ github.com/libregraph/idm/server
github.com/libregraph/idm/server/handler
github.com/libregraph/idm/server/handler/boltdb
github.com/libregraph/idm/server/handler/ldif
# github.com/libregraph/lico v0.62.0
# github.com/libregraph/lico v0.64.0
## explicit; go 1.18
github.com/libregraph/lico
github.com/libregraph/lico/bootstrap
@@ -1723,7 +1723,7 @@ github.com/rogpeppe/go-internal/internal/syscall/windows/sysdll
github.com/rogpeppe/go-internal/lockedfile
github.com/rogpeppe/go-internal/lockedfile/internal/filelock
github.com/rogpeppe/go-internal/semver
# github.com/rs/cors v1.11.0
# github.com/rs/cors v1.11.1
## explicit; go 1.13
github.com/rs/cors
github.com/rs/cors/internal