Skip to content
This repository was archived by the owner on Oct 9, 2023. It is now read-only.

Commit a853dac

Browse files
authored
Add oauth http proxy for external server & Extract email from azure claim (#553)
Signed-off-by: byhsu <[email protected]> Add oauth http proxy for external server because in on-prem cluster use cases, the client might not have access to the external network Extract email from azure claim
1 parent fc3db04 commit a853dac

13 files changed

+90
-17
lines changed

auth/authzserver/claims_verifier.go

+7
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ func verifyClaims(expectedAudience sets.String, claimsRaw map[string]interface{}
5858
}
5959
}
6060

61+
EmailKey := "email"
62+
// In some cases, "user_info" field doesn't exist in the raw claim,
63+
// but we can get email from "email" field
64+
if emailClaim, found := claimsRaw[EmailKey]; found {
65+
email := emailClaim.(string)
66+
userInfo.Email = email
67+
}
6168
// If this is a user-only access token with no scopes defined then add `all` scope by default because it's equivalent
6269
// to having a user's login cookie or an ID Token as means of accessing the service.
6370
if len(clientID) == 0 && scopes.Len() == 0 {

auth/authzserver/claims_verifier_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ func Test_verifyClaims(t *testing.T) {
2222
"sub": "123",
2323
"client_id": "my-client",
2424
"scp": []interface{}{"all", "offline"},
25+
"email": "[email protected]",
2526
})
2627

2728
assert.NoError(t, err)
2829
assert.Equal(t, sets.NewString("all", "offline"), identityCtx.Scopes())
2930
assert.Equal(t, "my-client", identityCtx.AppID())
3031
assert.Equal(t, "123", identityCtx.UserID())
3132
assert.Equal(t, "https://myserver", identityCtx.Audience())
33+
assert.Equal(t, "[email protected]", identityCtx.UserInfo().Email)
3234
})
3335

3436
t.Run("Multiple audience", func(t *testing.T) {

auth/authzserver/initialize.go

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
// RegisterHandlers registers http endpoints for handling OAuth2 flow (/authorize,
1717
func RegisterHandlers(handler interfaces.HandlerRegisterer, authCtx interfaces.AuthenticationContext) {
18+
// If using flyte self auth server, OAuth2Provider != nil
1819
if authCtx.OAuth2Provider() != nil {
1920
// Set up oauthserver endpoints. You could also use gorilla/mux or any other router.
2021
handler.HandleFunc(authorizeRelativeURL.String(), getAuthEndpoint(authCtx))

auth/authzserver/metadata_provider.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,17 @@ func (s OAuth2MetadataProvider) GetOAuth2Metadata(ctx context.Context, r *servic
6464
externalMetadataURL = baseURL.ResolveReference(oauth2MetadataEndpoint)
6565
}
6666

67-
response, err := http.Get(externalMetadataURL.String())
67+
httpClient := &http.Client{}
68+
69+
if len(s.cfg.HTTPProxyURL.String()) > 0 {
70+
// create a transport that uses the proxy
71+
transport := &http.Transport{
72+
Proxy: http.ProxyURL(&s.cfg.HTTPProxyURL.URL),
73+
}
74+
httpClient.Transport = transport
75+
}
76+
77+
response, err := httpClient.Get(externalMetadataURL.String())
6878
if err != nil {
6979
return nil, err
7080
}

auth/authzserver/resource_server.go

+18-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"golang.org/x/oauth2"
2424
)
2525

26+
// External auth server implementation
27+
2628
// ResourceServer authorizes access requests issued by an external Authorization Server.
2729
type ResourceServer struct {
2830
signatureVerifier oidc.KeySet
@@ -68,7 +70,9 @@ func unmarshalResp(r *http.Response, body []byte, v interface{}) error {
6870
return fmt.Errorf("expected Content-Type = application/json, got %q: %v", ct, err)
6971
}
7072

71-
func getJwksForIssuer(ctx context.Context, issuerBaseURL url.URL, customMetadataURL url.URL) (keySet oidc.KeySet, err error) {
73+
func getJwksForIssuer(ctx context.Context, issuerBaseURL url.URL, cfg authConfig.ExternalAuthorizationServer) (keySet oidc.KeySet, err error) {
74+
customMetadataURL := cfg.MetadataEndpointURL.URL
75+
7276
issuerBaseURL.Path = strings.TrimSuffix(issuerBaseURL.Path, "/") + "/"
7377
var wellKnown *url.URL
7478
if len(customMetadataURL.String()) > 0 {
@@ -77,12 +81,22 @@ func getJwksForIssuer(ctx context.Context, issuerBaseURL url.URL, customMetadata
7781
wellKnown = issuerBaseURL.ResolveReference(oauth2MetadataEndpoint)
7882
}
7983

84+
httpClient := &http.Client{}
85+
86+
if len(cfg.HTTPProxyURL.String()) > 0 {
87+
// create a transport that uses the proxy
88+
transport := &http.Transport{
89+
Proxy: http.ProxyURL(&cfg.HTTPProxyURL.URL),
90+
}
91+
httpClient.Transport = transport
92+
}
93+
8094
req, err := http.NewRequest(http.MethodGet, wellKnown.String(), nil)
8195
if err != nil {
8296
return nil, err
8397
}
8498

85-
resp, err := doRequest(ctx, req)
99+
resp, err := httpClient.Do(req)
86100
if err != nil {
87101
return nil, err
88102
}
@@ -104,7 +118,7 @@ func getJwksForIssuer(ctx context.Context, issuerBaseURL url.URL, customMetadata
104118
return nil, fmt.Errorf("failed to decode provider discovery object: %v", err)
105119
}
106120

107-
return oidc.NewRemoteKeySet(ctx, p.JwksUri), nil
121+
return oidc.NewRemoteKeySet(oidc.ClientContext(ctx, httpClient), p.JwksUri), nil
108122
}
109123

110124
// NewOAuth2ResourceServer initializes a new OAuth2ResourceServer.
@@ -114,7 +128,7 @@ func NewOAuth2ResourceServer(ctx context.Context, cfg authConfig.ExternalAuthori
114128
u = fallbackBaseURL
115129
}
116130

117-
verifier, err := getJwksForIssuer(ctx, u.URL, cfg.MetadataEndpointURL.URL)
131+
verifier, err := getJwksForIssuer(ctx, u.URL, cfg)
118132
if err != nil {
119133
return ResourceServer{}, err
120134
}

auth/authzserver/resource_server_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ func Test_getJwksForIssuer(t *testing.T) {
222222
type args struct {
223223
ctx context.Context
224224
issuerBaseURL url.URL
225-
customMetaURL url.URL
225+
cfg authConfig.ExternalAuthorizationServer
226226
}
227227
tests := []struct {
228228
name string
@@ -234,7 +234,7 @@ func Test_getJwksForIssuer(t *testing.T) {
234234
}
235235
for _, tt := range tests {
236236
t.Run(tt.name, func(t *testing.T) {
237-
got, err := getJwksForIssuer(tt.args.ctx, tt.args.issuerBaseURL, tt.args.customMetaURL)
237+
got, err := getJwksForIssuer(tt.args.ctx, tt.args.issuerBaseURL, tt.args.cfg)
238238
if (err != nil) != tt.wantErr {
239239
t.Errorf("getJwksForIssuer() error = %v, wantErr %v", err, tt.wantErr)
240240
return

auth/config/config.go

+5
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ type Config struct {
152152
// the `secure` setting.
153153
AuthorizedURIs []config.URL `json:"authorizedUris" pflag:"-,Optional: Defines the set of URIs that clients are allowed to visit the service on. If set, the system will attempt to match the incoming host to the first authorized URIs and use that (including the scheme) when generating metadata endpoints and when validating audience and issuer claims. If not provided, the urls will be deduced based on the request url and the 'secure' setting."`
154154

155+
// HTTPProxyURL allows operators to access external OAuth2 servers using an external HTTP Proxy
156+
HTTPProxyURL config.URL `json:"httpProxyURL" pflag:",OPTIONAL: HTTP Proxy to be used for OAuth requests."`
157+
155158
// UserAuth settings used to authenticate end users in web-browsers.
156159
UserAuth UserAuthConfig `json:"userAuth" pflag:",Defines Auth options for users."`
157160

@@ -187,6 +190,8 @@ type ExternalAuthorizationServer struct {
187190
BaseURL config.URL `json:"baseUrl" pflag:",This should be the base url of the authorization server that you are trying to hit. With Okta for instance, it will look something like https://company.okta.com/oauth2/abcdef123456789/"`
188191
AllowedAudience []string `json:"allowedAudience" pflag:",Optional: A list of allowed audiences. If not provided, the audience is expected to be the public Uri of the service."`
189192
MetadataEndpointURL config.URL `json:"metadataUrl" pflag:",Optional: If the server doesn't support /.well-known/oauth-authorization-server, you can set a custom metadata url here.'"`
193+
// HTTPProxyURL allows operators to access external OAuth2 servers using an external HTTP Proxy
194+
HTTPProxyURL config.URL `json:"httpProxyURL" pflag:",OPTIONAL: HTTP Proxy to be used for OAuth requests."`
190195
}
191196

192197
// OAuth2Options defines settings for app auth.

auth/config/config_flags.go

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

auth/config/config_flags_test.go

+28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

auth/handlers.go

+2
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ func GetHTTPRequestCookieToMetadataHandler(authCtx interfaces.AuthenticationCont
309309
return nil
310310
}
311311

312+
// IDtoken is injected into grpc authorization metadata
312313
meta := metadata.MD{
313314
DefaultAuthorizationHeader: []string{fmt.Sprintf("%s %s", IDTokenScheme, idToken)},
314315
}
@@ -396,6 +397,7 @@ func QueryUserInfo(ctx context.Context, identityContext interfaces.IdentityConte
396397
return QueryUserInfoUsingAccessToken(ctx, request, authCtx, accessToken)
397398
}
398399

400+
// Extract User info from access token for HTTP request
399401
func QueryUserInfoUsingAccessToken(ctx context.Context, originalRequest *http.Request, authCtx interfaces.AuthenticationContext, accessToken string) (
400402
*service.UserInfoResponse, error) {
401403

auth/interceptor.go

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package auth
33
import (
44
"context"
55

6+
"github.com/flyteorg/flytestdlib/logger"
67
"google.golang.org/grpc"
78
"google.golang.org/grpc/codes"
89
"google.golang.org/grpc/status"
@@ -17,6 +18,7 @@ func BlanketAuthorization(ctx context.Context, req interface{}, _ *grpc.UnarySer
1718
}
1819

1920
if !identityContext.Scopes().Has(ScopeAll) {
21+
logger.Debugf(ctx, "authenticated user doesn't have required scope")
2022
return nil, status.Errorf(codes.Unauthenticated, "authenticated user doesn't have required scope")
2123
}
2224

go.mod

+3-3
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,12 @@ require (
170170
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
171171
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
172172
golang.org/x/net v0.7.0 // indirect
173-
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
173+
golang.org/x/sync v0.1.0 // indirect
174174
golang.org/x/sys v0.5.0 // indirect
175175
golang.org/x/term v0.5.0 // indirect
176176
golang.org/x/text v0.7.0 // indirect
177-
golang.org/x/tools v0.1.12 // indirect
178-
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
177+
golang.org/x/tools v0.4.0 // indirect
178+
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
179179
google.golang.org/appengine v1.6.7 // indirect
180180
gopkg.in/inf.v0 v0.9.1 // indirect
181181
gopkg.in/ini.v1 v1.66.4 // indirect

go.sum

+7-7
Original file line numberDiff line numberDiff line change
@@ -1644,7 +1644,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
16441644
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
16451645
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
16461646
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
1647-
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
1647+
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
16481648
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
16491649
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
16501650
golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1751,8 +1751,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
17511751
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
17521752
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
17531753
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1754-
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
1755-
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1754+
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
1755+
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
17561756
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
17571757
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
17581758
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1998,17 +1998,17 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
19981998
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
19991999
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
20002000
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
2001-
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
2002-
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
2001+
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
2002+
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
20032003
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
20042004
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
20052005
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
20062006
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
20072007
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
20082008
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
20092009
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
2010-
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
2011-
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
2010+
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
2011+
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
20122012
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
20132013
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
20142014
gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=

0 commit comments

Comments
 (0)