diff --git a/client/cmd/login.go b/client/cmd/login.go index 2331e0b63..1b9e53f05 100644 --- a/client/cmd/login.go +++ b/client/cmd/login.go @@ -169,7 +169,8 @@ func foregroundGetTokenInfo(ctx context.Context, cmd *cobra.Command, config *int hostedClient := internal.NewHostedDeviceFlow( providerConfig.ProviderConfig.Audience, providerConfig.ProviderConfig.ClientID, - providerConfig.ProviderConfig.Domain, + providerConfig.ProviderConfig.TokenEndpoint, + providerConfig.ProviderConfig.DeviceAuthEndpoint, ) flowInfo, err := hostedClient.RequestDeviceCode(context.TODO()) diff --git a/client/internal/config.go b/client/internal/config.go index 18eef58fc..c573160b8 100644 --- a/client/internal/config.go +++ b/client/internal/config.go @@ -197,9 +197,14 @@ type ProviderConfig struct { // ClientSecret An IDP application client secret ClientSecret string // Domain An IDP API domain + // Deprecated. Use OIDCConfigEndpoint instead Domain string // Audience An Audience for to authorization validation Audience string + // TokenEndpoint is the endpoint of an IDP manager where clients can obtain access token + TokenEndpoint string + // DeviceAuthEndpoint is the endpoint of an IDP manager where clients can obtain device authorization code + DeviceAuthEndpoint string } func GetDeviceAuthorizationFlowInfo(ctx context.Context, config *Config) (DeviceAuthorizationFlow, error) { @@ -250,10 +255,12 @@ func GetDeviceAuthorizationFlowInfo(ctx context.Context, config *Config) (Device Provider: protoDeviceAuthorizationFlow.Provider.String(), ProviderConfig: ProviderConfig{ - Audience: protoDeviceAuthorizationFlow.ProviderConfig.Audience, - ClientID: protoDeviceAuthorizationFlow.ProviderConfig.ClientID, - ClientSecret: protoDeviceAuthorizationFlow.ProviderConfig.ClientSecret, - Domain: protoDeviceAuthorizationFlow.ProviderConfig.Domain, + Audience: protoDeviceAuthorizationFlow.GetProviderConfig().GetAudience(), + ClientID: protoDeviceAuthorizationFlow.GetProviderConfig().GetClientID(), + ClientSecret: protoDeviceAuthorizationFlow.GetProviderConfig().GetClientSecret(), + Domain: protoDeviceAuthorizationFlow.GetProviderConfig().Domain, + TokenEndpoint: protoDeviceAuthorizationFlow.GetProviderConfig().GetTokenEndpoint(), + DeviceAuthEndpoint: protoDeviceAuthorizationFlow.GetProviderConfig().GetDeviceAuthEndpoint(), }, }, nil } diff --git a/client/internal/oauth.go b/client/internal/oauth.go index 0249b239d..c80224212 100644 --- a/client/internal/oauth.go +++ b/client/internal/oauth.go @@ -5,8 +5,10 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" + "net/url" + "reflect" "strings" "time" ) @@ -14,7 +16,6 @@ import ( // OAuthClient is a OAuth client interface for various idp providers type OAuthClient interface { RequestDeviceCode(ctx context.Context) (DeviceAuthInfo, error) - RotateAccessToken(ctx context.Context, refreshToken string) (TokenInfo, error) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, error) GetClientID(ctx context.Context) string } @@ -55,8 +56,10 @@ type Hosted struct { Audience string // Hosted Native application client id ClientID string - // Hosted domain - Domain string + // TokenEndpoint to request access token + TokenEndpoint string + // DeviceAuthEndpoint to request device authorization code + DeviceAuthEndpoint string HTTPClient HTTPClient } @@ -84,11 +87,11 @@ type TokenRequestResponse struct { // Claims used when validating the access token type Claims struct { - Audience string `json:"aud"` + Audience interface{} `json:"aud"` } // NewHostedDeviceFlow returns an Hosted OAuth client -func NewHostedDeviceFlow(audience string, clientID string, domain string) *Hosted { +func NewHostedDeviceFlow(audience string, clientID string, tokenEndpoint string, deviceAuthEndpoint string) *Hosted { httpTransport := http.DefaultTransport.(*http.Transport).Clone() httpTransport.MaxIdleConns = 5 @@ -98,10 +101,11 @@ func NewHostedDeviceFlow(audience string, clientID string, domain string) *Hoste } return &Hosted{ - Audience: audience, - ClientID: clientID, - Domain: domain, - HTTPClient: httpClient, + Audience: audience, + ClientID: clientID, + TokenEndpoint: tokenEndpoint, + HTTPClient: httpClient, + DeviceAuthEndpoint: deviceAuthEndpoint, } } @@ -112,22 +116,15 @@ func (h *Hosted) GetClientID(ctx context.Context) string { // RequestDeviceCode requests a device code login flow information from Hosted func (h *Hosted) RequestDeviceCode(ctx context.Context) (DeviceAuthInfo, error) { - url := "https://" + h.Domain + "/oauth/device/code" - codePayload := RequestDeviceCodePayload{ - Audience: h.Audience, - ClientID: h.ClientID, - } - p, err := json.Marshal(codePayload) - if err != nil { - return DeviceAuthInfo{}, fmt.Errorf("parsing payload failed with error: %v", err) - } - payload := strings.NewReader(string(p)) - req, err := http.NewRequest("POST", url, payload) + form := url.Values{} + form.Add("client_id", h.ClientID) + form.Add("audience", h.Audience) + req, err := http.NewRequest("POST", h.DeviceAuthEndpoint, + strings.NewReader(form.Encode())) if err != nil { return DeviceAuthInfo{}, fmt.Errorf("creating request failed with error: %v", err) } - - req.Header.Add("content-type", "application/json") + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := h.HTTPClient.Do(req) if err != nil { @@ -135,7 +132,7 @@ func (h *Hosted) RequestDeviceCode(ctx context.Context) (DeviceAuthInfo, error) } defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) if err != nil { return DeviceAuthInfo{}, fmt.Errorf("reading body failed with error: %v", err) } @@ -153,6 +150,48 @@ func (h *Hosted) RequestDeviceCode(ctx context.Context) (DeviceAuthInfo, error) return deviceCode, err } +func (h *Hosted) requestToken(info DeviceAuthInfo) (TokenRequestResponse, error) { + form := url.Values{} + form.Add("client_id", h.ClientID) + form.Add("grant_type", HostedGrantType) + form.Add("device_code", info.DeviceCode) + req, err := http.NewRequest("POST", h.TokenEndpoint, strings.NewReader(form.Encode())) + if err != nil { + return TokenRequestResponse{}, fmt.Errorf("failed to create request access token: %v", err) + } + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + res, err := h.HTTPClient.Do(req) + if err != nil { + return TokenRequestResponse{}, fmt.Errorf("failed to request access token with error: %v", err) + } + + defer func() { + err := res.Body.Close() + if err != nil { + return + } + }() + + body, err := io.ReadAll(res.Body) + if err != nil { + return TokenRequestResponse{}, fmt.Errorf("failed reading access token response body with error: %v", err) + } + + if res.StatusCode > 499 { + return TokenRequestResponse{}, fmt.Errorf("access token response returned code: %s", string(body)) + } + + tokenResponse := TokenRequestResponse{} + err = json.Unmarshal(body, &tokenResponse) + if err != nil { + return TokenRequestResponse{}, fmt.Errorf("parsing token response failed with error: %v", err) + } + + return tokenResponse, nil +} + // WaitToken waits user's login and authorize the app. Once the user's authorize // it retrieves the access token from Hosted's endpoint and validates it before returning func (h *Hosted) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, error) { @@ -163,24 +202,8 @@ func (h *Hosted) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, case <-ctx.Done(): return TokenInfo{}, ctx.Err() case <-ticker.C: - url := "https://" + h.Domain + "/oauth/token" - tokenReqPayload := TokenRequestPayload{ - GrantType: HostedGrantType, - DeviceCode: info.DeviceCode, - ClientID: h.ClientID, - } - body, statusCode, err := requestToken(h.HTTPClient, url, tokenReqPayload) - if err != nil { - return TokenInfo{}, fmt.Errorf("wait for token: %v", err) - } - - if statusCode > 499 { - return TokenInfo{}, fmt.Errorf("wait token code returned error: %s", string(body)) - } - - tokenResponse := TokenRequestResponse{} - err = json.Unmarshal(body, &tokenResponse) + tokenResponse, err := h.requestToken(info) if err != nil { return TokenInfo{}, fmt.Errorf("parsing token response failed with error: %v", err) } @@ -214,71 +237,6 @@ func (h *Hosted) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, } } -// RotateAccessToken requests a new token using an existing refresh token -func (h *Hosted) RotateAccessToken(ctx context.Context, refreshToken string) (TokenInfo, error) { - url := "https://" + h.Domain + "/oauth/token" - tokenReqPayload := TokenRequestPayload{ - GrantType: HostedRefreshGrant, - ClientID: h.ClientID, - RefreshToken: refreshToken, - } - - body, statusCode, err := requestToken(h.HTTPClient, url, tokenReqPayload) - if err != nil { - return TokenInfo{}, fmt.Errorf("rotate access token: %v", err) - } - - if statusCode != 200 { - return TokenInfo{}, fmt.Errorf("rotating token returned error: %s", string(body)) - } - - tokenResponse := TokenRequestResponse{} - err = json.Unmarshal(body, &tokenResponse) - if err != nil { - return TokenInfo{}, fmt.Errorf("parsing token response failed with error: %v", err) - } - - err = isValidAccessToken(tokenResponse.AccessToken, h.Audience) - if err != nil { - return TokenInfo{}, fmt.Errorf("validate access token failed with error: %v", err) - } - - tokenInfo := TokenInfo{ - AccessToken: tokenResponse.AccessToken, - TokenType: tokenResponse.TokenType, - RefreshToken: tokenResponse.RefreshToken, - IDToken: tokenResponse.IDToken, - ExpiresIn: tokenResponse.ExpiresIn, - } - return tokenInfo, err -} - -func requestToken(client HTTPClient, url string, tokenReqPayload TokenRequestPayload) ([]byte, int, error) { - p, err := json.Marshal(tokenReqPayload) - if err != nil { - return nil, 0, fmt.Errorf("parsing token payload failed with error: %v", err) - } - payload := strings.NewReader(string(p)) - req, err := http.NewRequest("POST", url, payload) - if err != nil { - return nil, 0, fmt.Errorf("creating token request failed with error: %v", err) - } - - req.Header.Add("content-type", "application/json") - - res, err := client.Do(req) - if err != nil { - return nil, 0, fmt.Errorf("doing token request failed with error: %v", err) - } - - defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, 0, fmt.Errorf("reading token body failed with error: %v", err) - } - return body, res.StatusCode, nil -} - // isValidAccessToken is a simple validation of the access token func isValidAccessToken(token string, audience string) error { if token == "" { @@ -297,9 +255,24 @@ func isValidAccessToken(token string, audience string) error { return err } - if claims.Audience != audience { - return fmt.Errorf("invalid audience") + if claims.Audience == nil { + return fmt.Errorf("required token field audience is absent") } - return nil + // Audience claim of JWT can be a string or an array of strings + typ := reflect.TypeOf(claims.Audience) + switch typ.Kind() { + case reflect.String: + if claims.Audience == audience { + return nil + } + case reflect.Slice: + for _, aud := range claims.Audience.([]interface{}) { + if audience == aud { + return nil + } + } + } + + return fmt.Errorf("invalid JWT token audience field") } diff --git a/client/internal/oauth_test.go b/client/internal/oauth_test.go index b35ff9558..fe22b1bac 100644 --- a/client/internal/oauth_test.go +++ b/client/internal/oauth_test.go @@ -2,12 +2,12 @@ package internal import ( "context" - "encoding/json" "fmt" "github.com/golang-jwt/jwt" "github.com/stretchr/testify/require" - "io/ioutil" + "io" "net/http" + "net/url" "strings" "testing" "time" @@ -24,7 +24,7 @@ type mockHTTPClient struct { } func (c *mockHTTPClient) Do(req *http.Request) (*http.Response, error) { - body, err := ioutil.ReadAll(req.Body) + body, err := io.ReadAll(req.Body) if err == nil { c.reqBody = string(body) } @@ -33,13 +33,13 @@ func (c *mockHTTPClient) Do(req *http.Request) (*http.Response, error) { c.count++ return &http.Response{ StatusCode: c.code, - Body: ioutil.NopCloser(strings.NewReader(c.countResBody)), + Body: io.NopCloser(strings.NewReader(c.countResBody)), }, c.err } return &http.Response{ StatusCode: c.code, - Body: ioutil.NopCloser(strings.NewReader(c.resBody)), + Body: io.NopCloser(strings.NewReader(c.resBody)), }, c.err } @@ -54,15 +54,19 @@ func TestHosted_RequestDeviceCode(t *testing.T) { testingFunc require.ComparisonAssertionFunc expectedOut DeviceAuthInfo expectedMSG string - expectPayload RequestDeviceCodePayload + expectPayload string } + expectedAudience := "ok" + expectedClientID := "bla" + form := url.Values{} + form.Add("audience", expectedAudience) + form.Add("client_id", expectedClientID) + expectPayload := form.Encode() + testCase1 := test{ - name: "Payload Is Valid", - expectPayload: RequestDeviceCodePayload{ - Audience: "ok", - ClientID: "bla", - }, + name: "Payload Is Valid", + expectPayload: expectPayload, inputReqCode: 200, testingErrFunc: require.Error, testingFunc: require.EqualValues, @@ -74,6 +78,7 @@ func TestHosted_RequestDeviceCode(t *testing.T) { testingErrFunc: require.Error, expectedErrorMSG: "should return error", testingFunc: require.EqualValues, + expectPayload: expectPayload, } testCase3 := test{ @@ -82,15 +87,13 @@ func TestHosted_RequestDeviceCode(t *testing.T) { testingErrFunc: require.Error, expectedErrorMSG: "should return error", testingFunc: require.EqualValues, + expectPayload: expectPayload, } testCase4Out := DeviceAuthInfo{ExpiresIn: 10} testCase4 := test{ - name: "Got Device Code", - inputResBody: fmt.Sprintf("{\"expires_in\":%d}", testCase4Out.ExpiresIn), - expectPayload: RequestDeviceCodePayload{ - Audience: "ok", - ClientID: "bla", - }, + name: "Got Device Code", + inputResBody: fmt.Sprintf("{\"expires_in\":%d}", testCase4Out.ExpiresIn), + expectPayload: expectPayload, inputReqCode: 200, testingErrFunc: require.NoError, testingFunc: require.EqualValues, @@ -108,18 +111,17 @@ func TestHosted_RequestDeviceCode(t *testing.T) { } hosted := Hosted{ - Audience: testCase.expectPayload.Audience, - ClientID: testCase.expectPayload.ClientID, - Domain: "test.hosted.com", - HTTPClient: &httpClient, + Audience: expectedAudience, + ClientID: expectedClientID, + TokenEndpoint: "test.hosted.com/token", + DeviceAuthEndpoint: "test.hosted.com/device/auth", + HTTPClient: &httpClient, } authInfo, err := hosted.RequestDeviceCode(context.TODO()) testCase.testingErrFunc(t, err, testCase.expectedErrorMSG) - payload, _ := json.Marshal(testCase.expectPayload) - - require.EqualValues(t, string(payload), httpClient.reqBody, "payload should match") + require.EqualValues(t, expectPayload, httpClient.reqBody, "payload should match") testCase.testingFunc(t, testCase.expectedOut, authInfo, testCase.expectedMSG) @@ -143,7 +145,7 @@ func TestHosted_WaitToken(t *testing.T) { testingFunc require.ComparisonAssertionFunc expectedOut TokenInfo expectedMSG string - expectPayload TokenRequestPayload + expectPayload string } defaultInfo := DeviceAuthInfo{ @@ -152,11 +154,13 @@ func TestHosted_WaitToken(t *testing.T) { Interval: 1, } - tokenReqPayload := TokenRequestPayload{ - GrantType: HostedGrantType, - DeviceCode: defaultInfo.DeviceCode, - ClientID: "test", - } + clientID := "test" + + form := url.Values{} + form.Add("grant_type", HostedGrantType) + form.Add("device_code", defaultInfo.DeviceCode) + form.Add("client_id", clientID) + tokenReqPayload := form.Encode() testCase1 := test{ name: "Payload Is Valid", @@ -268,10 +272,11 @@ func TestHosted_WaitToken(t *testing.T) { } hosted := Hosted{ - Audience: testCase.inputAudience, - ClientID: testCase.expectPayload.ClientID, - Domain: "test.hosted.com", - HTTPClient: &httpClient, + Audience: testCase.inputAudience, + ClientID: clientID, + TokenEndpoint: "test.hosted.com/token", + DeviceAuthEndpoint: "test.hosted.com/device/auth", + HTTPClient: &httpClient, } ctx, cancel := context.WithTimeout(context.TODO(), testCase.inputTimeout) @@ -279,12 +284,7 @@ func TestHosted_WaitToken(t *testing.T) { tokenInfo, err := hosted.WaitToken(ctx, testCase.inputInfo) testCase.testingErrFunc(t, err, testCase.expectedErrorMSG) - var payload []byte - var emptyPayload TokenRequestPayload - if testCase.expectPayload != emptyPayload { - payload, _ = json.Marshal(testCase.expectPayload) - } - require.EqualValues(t, string(payload), httpClient.reqBody, "payload should match") + require.EqualValues(t, testCase.expectPayload, httpClient.reqBody, "payload should match") testCase.testingFunc(t, testCase.expectedOut, tokenInfo, testCase.expectedMSG) @@ -293,123 +293,3 @@ func TestHosted_WaitToken(t *testing.T) { }) } } - -func TestHosted_RotateAccessToken(t *testing.T) { - type test struct { - name string - inputResBody string - inputReqCode int - inputReqError error - inputMaxReqs int - inputInfo DeviceAuthInfo - inputAudience string - testingErrFunc require.ErrorAssertionFunc - expectedErrorMSG string - testingFunc require.ComparisonAssertionFunc - expectedOut TokenInfo - expectedMSG string - expectPayload TokenRequestPayload - } - - defaultInfo := DeviceAuthInfo{ - DeviceCode: "test", - ExpiresIn: 10, - Interval: 1, - } - - tokenReqPayload := TokenRequestPayload{ - GrantType: HostedRefreshGrant, - ClientID: "test", - RefreshToken: "refresh_test", - } - - testCase1 := test{ - name: "Payload Is Valid", - inputInfo: defaultInfo, - inputReqCode: 200, - testingErrFunc: require.Error, - testingFunc: require.EqualValues, - expectPayload: tokenReqPayload, - } - - testCase2 := test{ - name: "Exit On Network Error", - inputInfo: defaultInfo, - expectPayload: tokenReqPayload, - inputReqError: fmt.Errorf("error"), - testingErrFunc: require.Error, - expectedErrorMSG: "should return error", - testingFunc: require.EqualValues, - } - - testCase3 := test{ - name: "Exit On Non 200 Status Code", - inputInfo: defaultInfo, - inputReqCode: 401, - expectPayload: tokenReqPayload, - testingErrFunc: require.Error, - expectedErrorMSG: "should return error", - testingFunc: require.EqualValues, - } - - audience := "test" - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{"aud": audience}) - var hmacSampleSecret []byte - tokenString, _ := token.SignedString(hmacSampleSecret) - - testCase4 := test{ - name: "Exit On Invalid Audience", - inputInfo: defaultInfo, - inputResBody: fmt.Sprintf("{\"access_token\":\"%s\"}", tokenString), - inputReqCode: 200, - inputAudience: "super test", - testingErrFunc: require.Error, - testingFunc: require.EqualValues, - expectPayload: tokenReqPayload, - } - - testCase5 := test{ - name: "Received Token Info", - inputInfo: defaultInfo, - inputResBody: fmt.Sprintf("{\"access_token\":\"%s\"}", tokenString), - inputReqCode: 200, - inputAudience: audience, - testingErrFunc: require.NoError, - testingFunc: require.EqualValues, - expectPayload: tokenReqPayload, - expectedOut: TokenInfo{AccessToken: tokenString}, - } - - for _, testCase := range []test{testCase1, testCase2, testCase3, testCase4, testCase5} { - t.Run(testCase.name, func(t *testing.T) { - - httpClient := mockHTTPClient{ - resBody: testCase.inputResBody, - code: testCase.inputReqCode, - err: testCase.inputReqError, - MaxReqs: testCase.inputMaxReqs, - } - - hosted := Hosted{ - Audience: testCase.inputAudience, - ClientID: testCase.expectPayload.ClientID, - Domain: "test.hosted.com", - HTTPClient: &httpClient, - } - - tokenInfo, err := hosted.RotateAccessToken(context.TODO(), testCase.expectPayload.RefreshToken) - testCase.testingErrFunc(t, err, testCase.expectedErrorMSG) - - var payload []byte - var emptyPayload TokenRequestPayload - if testCase.expectPayload != emptyPayload { - payload, _ = json.Marshal(testCase.expectPayload) - } - require.EqualValues(t, string(payload), httpClient.reqBody, "payload should match") - - testCase.testingFunc(t, testCase.expectedOut, tokenInfo, testCase.expectedMSG) - - }) - } -} diff --git a/client/main.go b/client/main.go index c03989a91..2a70590fe 100644 --- a/client/main.go +++ b/client/main.go @@ -1,9 +1,8 @@ package main import ( - "os" - "github.com/netbirdio/netbird/client/cmd" + "os" ) func main() { diff --git a/client/server/server.go b/client/server/server.go index fa702cef5..67c3b7ed8 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -208,7 +208,8 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro hostedClient := internal.NewHostedDeviceFlow( providerConfig.ProviderConfig.Audience, providerConfig.ProviderConfig.ClientID, - providerConfig.ProviderConfig.Domain, + providerConfig.ProviderConfig.TokenEndpoint, + providerConfig.ProviderConfig.DeviceAuthEndpoint, ) if s.oauthAuthFlow.client != nil && s.oauthAuthFlow.client.GetClientID(ctx) == hostedClient.GetClientID(context.TODO()) { diff --git a/management/cmd/management.go b/management/cmd/management.go index a0fc8921f..b83992fd4 100644 --- a/management/cmd/management.go +++ b/management/cmd/management.go @@ -2,6 +2,7 @@ package cmd import ( "crypto/tls" + "encoding/json" "errors" "flag" "fmt" @@ -13,6 +14,7 @@ import ( "io/fs" "net" "net/http" + "net/url" "os" "path" "strings" @@ -304,9 +306,94 @@ func loadMgmtConfig(mgmtConfigPath string) (*server.Config, error) { config.HttpConfig.CertKey = certKey } + oidcEndpoint := config.HttpConfig.OIDCConfigEndpoint + if oidcEndpoint != "" { + // if OIDCConfigEndpoint is specified, we can load DeviceAuthEndpoint and TokenEndpoint automatically + log.Infof("loading OIDC configuration from the provided IDP configuration endpoint %s", oidcEndpoint) + oidcConfig, err := fetchOIDCConfig(oidcEndpoint) + if err != nil { + return nil, err + } + log.Infof("loaded OIDC configuration from the provided IDP configuration endpoint: %s", oidcEndpoint) + + log.Infof("overriding HttpConfig.AuthIssuer with a new value %s, previously configured value: %s", + oidcConfig.Issuer, config.HttpConfig.AuthIssuer) + config.HttpConfig.AuthIssuer = oidcConfig.Issuer + + log.Infof("overriding HttpConfig.AuthKeysLocation (JWT certs) with a new value %s, previously configured value: %s", + oidcConfig.JwksURI, config.HttpConfig.AuthKeysLocation) + config.HttpConfig.AuthKeysLocation = oidcConfig.JwksURI + + if config.DeviceAuthorizationFlow != nil { + log.Infof("overriding DeviceAuthorizationFlow.TokenEndpoint with a new value: %s, previously configured value: %s", + oidcConfig.TokenEndpoint, config.DeviceAuthorizationFlow.ProviderConfig.TokenEndpoint) + config.DeviceAuthorizationFlow.ProviderConfig.TokenEndpoint = oidcConfig.TokenEndpoint + log.Infof("overriding DeviceAuthorizationFlow.DeviceAuthEndpoint with a new value: %s, previously configured value: %s", + oidcConfig.DeviceAuthEndpoint, config.DeviceAuthorizationFlow.ProviderConfig.DeviceAuthEndpoint) + config.DeviceAuthorizationFlow.ProviderConfig.DeviceAuthEndpoint = oidcConfig.DeviceAuthEndpoint + + u, err := url.Parse(oidcEndpoint) + if err != nil { + return nil, err + } + log.Infof("overriding DeviceAuthorizationFlow.ProviderConfig.Domain with a new value: %s, previously configured value: %s", + u.Host, config.DeviceAuthorizationFlow.ProviderConfig.Domain) + config.DeviceAuthorizationFlow.ProviderConfig.Domain = u.Host + + } + if config.IdpManagerConfig != nil { + log.Infof("overriding Auth0ClientCredentials.AuthIssuer with a new value: %s, previously configured value: %s", + oidcConfig.Issuer, config.IdpManagerConfig.Auth0ClientCredentials.AuthIssuer) + config.IdpManagerConfig.Auth0ClientCredentials.AuthIssuer = oidcConfig.Issuer + } + } + return config, err } +// OIDCConfigResponse used for parsing OIDC config response +type OIDCConfigResponse struct { + Issuer string `json:"issuer"` + TokenEndpoint string `json:"token_endpoint"` + DeviceAuthEndpoint string `json:"device_authorization_endpoint"` + JwksURI string `json:"jwks_uri"` +} + +// fetchOIDCConfig fetches OIDC configuration from the IDP +func fetchOIDCConfig(oidcEndpoint string) (OIDCConfigResponse, error) { + + res, err := http.Get(oidcEndpoint) + if err != nil { + return OIDCConfigResponse{}, fmt.Errorf("failed fetching OIDC configuration fro mendpoint %s %v", oidcEndpoint, err) + } + + defer func() { + err := res.Body.Close() + if err != nil { + log.Debugf("failed closing response body %v", err) + } + }() + + body, err := io.ReadAll(res.Body) + if err != nil { + return OIDCConfigResponse{}, fmt.Errorf("failed reading OIDC configuration response body: %v", err) + } + + if res.StatusCode != 200 { + return OIDCConfigResponse{}, fmt.Errorf("OIDC configuration request returned status %d with response: %s", + res.StatusCode, string(body)) + } + + config := OIDCConfigResponse{} + err = json.Unmarshal(body, &config) + if err != nil { + return OIDCConfigResponse{}, fmt.Errorf("failed unmarshaling OIDC configuration response: %v", err) + } + + return config, nil + +} + func loadTLSConfig(certFile string, certKey string) (*tls.Config, error) { // Load server's certificate and private key serverCert, err := tls.LoadX509KeyPair(certFile, certKey) diff --git a/management/proto/management.pb.go b/management/proto/management.pb.go index d9cad58e3..89bdcf0bd 100644 --- a/management/proto/management.pb.go +++ b/management/proto/management.pb.go @@ -1,15 +1,15 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.21.2 +// protoc v3.12.4 // source: management.proto package proto import ( + timestamp "github.com/golang/protobuf/ptypes/timestamp" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -611,7 +611,7 @@ type ServerKeyResponse struct { // Server's Wireguard public key Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` // Key expiration timestamp after which the key should be fetched again by the client - ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"` + ExpiresAt *timestamp.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"` // Version of the Wiretrustee Management Service protocol Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` } @@ -655,7 +655,7 @@ func (x *ServerKeyResponse) GetKey() string { return "" } -func (x *ServerKeyResponse) GetExpiresAt() *timestamppb.Timestamp { +func (x *ServerKeyResponse) GetExpiresAt() *timestamp.Timestamp { if x != nil { return x.ExpiresAt } @@ -1287,9 +1287,14 @@ type ProviderConfig struct { // An IDP application client secret ClientSecret string `protobuf:"bytes,2,opt,name=ClientSecret,proto3" json:"ClientSecret,omitempty"` // An IDP API domain + // Deprecated. Use a DeviceAuthEndpoint and TokenEndpoint Domain string `protobuf:"bytes,3,opt,name=Domain,proto3" json:"Domain,omitempty"` // An Audience for validation Audience string `protobuf:"bytes,4,opt,name=Audience,proto3" json:"Audience,omitempty"` + // DeviceAuthEndpoint is an endpoint to request device authentication code. + DeviceAuthEndpoint string `protobuf:"bytes,5,opt,name=DeviceAuthEndpoint,proto3" json:"DeviceAuthEndpoint,omitempty"` + // TokenEndpoint is an endpoint to request auth token. + TokenEndpoint string `protobuf:"bytes,6,opt,name=TokenEndpoint,proto3" json:"TokenEndpoint,omitempty"` } func (x *ProviderConfig) Reset() { @@ -1352,6 +1357,20 @@ func (x *ProviderConfig) GetAudience() string { return "" } +func (x *ProviderConfig) GetDeviceAuthEndpoint() string { + if x != nil { + return x.DeviceAuthEndpoint + } + return "" +} + +func (x *ProviderConfig) GetTokenEndpoint() string { + if x != nil { + return x.TokenEndpoint + } + return "" +} + // Route represents a route.Route object type Route struct { state protoimpl.MessageState @@ -1607,7 +1626,7 @@ var file_management_proto_rawDesc = []byte{ 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, - 0x10, 0x00, 0x22, 0x84, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, + 0x10, 0x00, 0x22, 0xda, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, @@ -1615,43 +1634,49 @@ var file_management_proto_rawDesc = []byte{ 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x05, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, - 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, - 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, - 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, - 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, - 0x44, 0x32, 0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, - 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, - 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, - 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, + 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, + 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, + 0xb5, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, + 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x32, 0xf7, 0x02, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, + 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, - 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, - 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, - 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, - 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, - 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, - 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, + 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, + 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, + 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, + 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, + 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1691,7 +1716,7 @@ var file_management_proto_goTypes = []interface{}{ (*DeviceAuthorizationFlow)(nil), // 19: management.DeviceAuthorizationFlow (*ProviderConfig)(nil), // 20: management.ProviderConfig (*Route)(nil), // 21: management.Route - (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp + (*timestamp.Timestamp)(nil), // 22: google.protobuf.Timestamp } var file_management_proto_depIdxs = []int32{ 11, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig diff --git a/management/proto/management.proto b/management/proto/management.proto index e67dd7488..b580c29ef 100644 --- a/management/proto/management.proto +++ b/management/proto/management.proto @@ -227,9 +227,14 @@ message ProviderConfig { // An IDP application client secret string ClientSecret = 2; // An IDP API domain - string Domain =3; + // Deprecated. Use a DeviceAuthEndpoint and TokenEndpoint + string Domain = 3; // An Audience for validation string Audience = 4; + // DeviceAuthEndpoint is an endpoint to request device authentication code. + string DeviceAuthEndpoint = 5; + // TokenEndpoint is an endpoint to request auth token. + string TokenEndpoint = 6; } // Route represents a route.Route object diff --git a/management/server/config.go b/management/server/config.go index edc8c6eb6..8e2ffd995 100644 --- a/management/server/config.go +++ b/management/server/config.go @@ -55,6 +55,8 @@ type HttpServerConfig struct { AuthIssuer string // AuthKeysLocation is a location of JWT key set containing the public keys used to verify JWT AuthKeysLocation string + // OIDCConfigEndpoint is the endpoint of an IDP manager to get OIDC configuration + OIDCConfigEndpoint string } // Host represents a Wiretrustee host (e.g. STUN, TURN, Signal) @@ -81,9 +83,14 @@ type ProviderConfig struct { // ClientSecret An IDP application client secret ClientSecret string // Domain An IDP API domain + // Deprecated. Use TokenEndpoint and DeviceAuthEndpoint Domain string // Audience An Audience for to authorization validation Audience string + // TokenEndpoint is the endpoint of an IDP manager where clients can obtain access token + TokenEndpoint string + // DeviceAuthEndpoint is the endpoint of an IDP manager where clients can obtain device authorization code + DeviceAuthEndpoint string } // validateURL validates input http url diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index e11f47302..b64909ea6 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -503,10 +503,12 @@ func (s *GRPCServer) GetDeviceAuthorizationFlow(ctx context.Context, req *proto. flowInfoResp := &proto.DeviceAuthorizationFlow{ Provider: proto.DeviceAuthorizationFlowProvider(provider), ProviderConfig: &proto.ProviderConfig{ - ClientID: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientID, - ClientSecret: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientSecret, - Domain: s.config.DeviceAuthorizationFlow.ProviderConfig.Domain, - Audience: s.config.DeviceAuthorizationFlow.ProviderConfig.Audience, + ClientID: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientID, + ClientSecret: s.config.DeviceAuthorizationFlow.ProviderConfig.ClientSecret, + Domain: s.config.DeviceAuthorizationFlow.ProviderConfig.Domain, + Audience: s.config.DeviceAuthorizationFlow.ProviderConfig.Audience, + DeviceAuthEndpoint: s.config.DeviceAuthorizationFlow.ProviderConfig.DeviceAuthEndpoint, + TokenEndpoint: s.config.DeviceAuthorizationFlow.ProviderConfig.TokenEndpoint, }, }