pomerium/pkg/identity/manager/data.go
Kenneth Jenkins 418ee79e1a
authenticate: rework session ID token handling (#5178)
Currently, the Session proto id_token field is populated with Pomerium
session data during initial login, but with IdP ID token data after an
IdP session refresh.

Instead, store only IdP ID token data in this field.

Update the existing SetRawIDToken method to populate the structured data
fields based on the contents of the raw ID token. Remove the other code
that sets these fields (in the authenticateflow package and in
manager.sessionUnmarshaler).

Add a test for the identity manager, exercising the combined effect of
session claims unmarshaling and SetRawIDToken(), to verify that the
combined behavior is preserved unchanged.
2024-07-29 12:43:50 -07:00

128 lines
2.6 KiB
Go

package manager
import (
"encoding/json"
"errors"
"time"
"github.com/pomerium/pomerium/pkg/grpc/session"
"github.com/pomerium/pomerium/pkg/grpc/user"
"github.com/pomerium/pomerium/pkg/identity"
)
func nextSessionRefresh(
s *session.Session,
lastRefresh time.Time,
gracePeriod time.Duration,
coolOffDuration time.Duration,
) time.Time {
var tm time.Time
if s.GetOauthToken().GetExpiresAt() != nil {
expiry := s.GetOauthToken().GetExpiresAt().AsTime()
if s.GetOauthToken().GetExpiresAt().IsValid() && !expiry.IsZero() {
expiry = expiry.Add(-gracePeriod)
if tm.IsZero() || expiry.Before(tm) {
tm = expiry
}
}
}
if s.GetExpiresAt() != nil {
expiry := s.GetExpiresAt().AsTime()
if s.GetExpiresAt().IsValid() && !expiry.IsZero() {
if tm.IsZero() || expiry.Before(tm) {
tm = expiry
}
}
}
// don't refresh any quicker than the cool-off duration
min := lastRefresh.Add(coolOffDuration)
if tm.Before(min) {
tm = min
}
return tm
}
// a multiUnmarshaler is used as the target of the json Unmarshal function to
// unmarshal a single JSON value into multiple destinations.
type multiUnmarshaler []any
func newMultiUnmarshaler(args ...any) *multiUnmarshaler {
return (*multiUnmarshaler)(&args)
}
func (dst *multiUnmarshaler) UnmarshalJSON(data []byte) error {
var err error
for _, o := range *dst {
if o != nil {
err = errors.Join(err, json.Unmarshal(data, o))
}
}
return err
}
type sessionUnmarshaler struct {
*session.Session
}
func newSessionUnmarshaler(s *session.Session) *sessionUnmarshaler {
return &sessionUnmarshaler{Session: s}
}
func (dst *sessionUnmarshaler) UnmarshalJSON(data []byte) error {
if dst.Session == nil {
return nil
}
var raw map[string]json.RawMessage
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
// To preserve existing behavior: filter out claims not related to user info.
delete(raw, "iss")
delete(raw, "sub")
delete(raw, "exp")
delete(raw, "iat")
dst.Session.AddClaims(identity.NewClaimsFromRaw(raw).Flatten())
return nil
}
type userUnmarshaler struct {
*user.User
}
func newUserUnmarshaler(u *user.User) *userUnmarshaler {
return &userUnmarshaler{User: u}
}
func (dst *userUnmarshaler) UnmarshalJSON(data []byte) error {
if dst.User == nil {
return nil
}
var raw map[string]json.RawMessage
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
if name, ok := raw["name"]; ok {
_ = json.Unmarshal(name, &dst.User.Name)
delete(raw, "name")
}
if email, ok := raw["email"]; ok {
_ = json.Unmarshal(email, &dst.User.Email)
delete(raw, "email")
}
dst.User.AddClaims(identity.NewClaimsFromRaw(raw).Flatten())
return nil
}