git.iko.soy/typeable/oauth2-proxy
on master help sign in
112 lines 3.3 kB history raw
1package providers
3import (
4 "context"
5 "fmt"
6 "net/url"
7 "strings"
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
11 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
14// ADFSProvider represents an ADFS based Identity Provider
15type ADFSProvider struct {
16 *OIDCProvider
18 skipScope bool
19 // Expose for unit testing
20 oidcEnrichFunc func(context.Context, *sessions.SessionState) error
21 oidcRefreshFunc func(context.Context, *sessions.SessionState) (bool, error)
24var _ Provider = (*ADFSProvider)(nil)
26const (
27 adfsProviderName = "ADFS"
28 adfsDefaultScope = "openid email profile"
29 adfsUPNClaim = "upn"
32// NewADFSProvider initiates a new ADFSProvider
33func NewADFSProvider(p *ProviderData, opts options.Provider) *ADFSProvider {
34 p.setProviderDefaults(providerDefaults{
35 name: adfsProviderName,
36 scope: adfsDefaultScope,
37 })
39 if p.ProtectedResource != nil && p.ProtectedResource.String() != "" {
40 resource := p.ProtectedResource.String()
41 if !strings.HasSuffix(resource, "/") {
42 resource += "/"
43 }
45 if p.Scope != "" && !strings.HasPrefix(p.Scope, resource) {
46 p.Scope = resource + p.Scope
47 }
48 }
50 oidcProvider := NewOIDCProvider(p, opts.OIDCConfig)
52 return &ADFSProvider{
53 OIDCProvider: oidcProvider,
54 skipScope: ptr.Deref(opts.ADFSConfig.SkipScope, options.DefaultADFSSkipScope),
55 oidcEnrichFunc: oidcProvider.EnrichSession,
56 oidcRefreshFunc: oidcProvider.RefreshSession,
57 }
60// GetLoginURL Override to double encode the state parameter. If not query params are lost
61// More info here: https://docs.microsoft.com/en-us/powerapps/maker/portals/configure/configure-saml2-settings
62func (p *ADFSProvider) GetLoginURL(redirectURI, state, nonce string, extraParams url.Values) string {
63 if !p.SkipNonce {
64 extraParams.Add("nonce", nonce)
65 }
66 loginURL := makeLoginURL(p.Data(), redirectURI, url.QueryEscape(state), extraParams)
67 if p.skipScope {
68 q := loginURL.Query()
69 q.Del("scope")
70 loginURL.RawQuery = q.Encode()
71 }
72 return loginURL.String()
75// EnrichSession calls the OIDC ProfileURL to backfill any fields missing
76// from the claims. If Email is missing, falls back to ADFS `upn` claim.
77func (p *ADFSProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
78 err := p.oidcEnrichFunc(ctx, s)
79 if err != nil || s.Email == "" {
80 // OIDC only errors if email is missing
81 return p.fallbackUPN(ctx, s)
82 }
83 return nil
86// RefreshSession refreshes via the OIDC implementation. If email is missing,
87// falls back to ADFS `upn` claim.
88func (p *ADFSProvider) RefreshSession(ctx context.Context, s *sessions.SessionState) (bool, error) {
89 refreshed, err := p.oidcRefreshFunc(ctx, s)
90 if err != nil || s.Email != "" {
91 return refreshed, err
92 }
93 err = p.fallbackUPN(ctx, s)
94 return refreshed, err
97func (p *ADFSProvider) fallbackUPN(_ context.Context, s *sessions.SessionState) error {
98 claims, err := p.getClaimExtractor(s.IDToken, s.AccessToken)
99 if err != nil {
100 return fmt.Errorf("could not extract claims: %v", err)
101 }
103 upn, found, err := claims.GetClaim(adfsUPNClaim)
104 if err != nil {
105 return fmt.Errorf("could not extract %s claim: %v", adfsUPNClaim, err)
106 }
108 if found && fmt.Sprint(upn) != "" {
109 s.Email = fmt.Sprint(upn)
110 }
111 return nil