Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Commit

Permalink
Generate user tokens in Auth service (#402)
Browse files Browse the repository at this point in the history
- New private keys ( added in openshift secrets ) in configuration (should match keys in KC)
- We don't load public keys from KC any more
- During login (/login and /token) we now generate our own keys (access & refresh) based on the KC -
 tokens instead of passing along the original KC tokens to client
- We also generate new tokens when refreshing the token (/token/refresh)
- Added new methods in token manager to generate tokens (access & refresh) based on Identity (in addition to methods to generate tokens based on KC tokens). These methods are not used atm but fully tested and ready to be used when we decommission KC
- /token/generate (in Dev Mode) now doesn't use KC
- /token/refresh in Dev Mode (but not in tests) doesn't use KC either

fixes #229
  • Loading branch information
alexeykazakov authored and sbose78 committed Mar 29, 2018
1 parent 198fad4 commit d39e42a
Show file tree
Hide file tree
Showing 32 changed files with 1,715 additions and 781 deletions.
2 changes: 1 addition & 1 deletion account/email/verification_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (c *EmailVerificationClient) SendVerificationCode(ctx context.Context, req
}

func (c *EmailVerificationClient) generateVerificationURL(ctx context.Context, req *goa.RequestData, code string) string {
return rest.AbsoluteURL(req, authclient.VerifyEmailUsersPath()) + "?code=" + code
return rest.AbsoluteURL(req, authclient.VerifyEmailUsersPath(), nil) + "?code=" + code
}

// VerifyCode validates whether the code is present in our database and returns a non-nil if yes.
Expand Down
4 changes: 2 additions & 2 deletions auth/authz_blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func (s *TestAuthSuite) TestGetEntitlement() {
require.Nil(s.T(), err)
tokenEndpoint, err := configuration.GetKeycloakEndpointToken(r)
require.Nil(s.T(), err)
testUserToken, err := controller.GenerateUserToken(ctx, tokenEndpoint, configuration, configuration.GetKeycloakTestUserName(), configuration.GetKeycloakTestUserSecret())
testUserToken, err := controller.ObtainKeycloakUserToken(ctx, tokenEndpoint, configuration, configuration.GetKeycloakTestUserName(), configuration.GetKeycloakTestUserSecret())
// {"permissions" : [{"resource_set_name" : "<spaceID>"}]}
entitlementResource := auth.EntitlementResource{
Permissions: []auth.ResourceSet{{Name: resourceName}},
Expand Down Expand Up @@ -426,7 +426,7 @@ func getUserID(t *testing.T, username string, usersecret string) string {
require.Nil(t, err)

ctx := context.Background()
testToken, err := controller.GenerateUserToken(ctx, tokenEndpoint, configuration, username, usersecret)
testToken, err := controller.ObtainKeycloakUserToken(ctx, tokenEndpoint, configuration, username, usersecret)
require.Nil(t, err)
accessToken := testToken.Token.AccessToken
require.NotNil(t, accessToken)
Expand Down
330 changes: 215 additions & 115 deletions configuration/configuration.go

Large diffs are not rendered by default.

81 changes: 54 additions & 27 deletions configuration/configuration_blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func resetConfiguration() {
}
}

func TestGetKeycloakEndpointSetByUrlEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointSetByUrlEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
env := os.Getenv("AUTH_KEYCLOAK_URL")
defer func() {
Expand Down Expand Up @@ -94,9 +94,9 @@ func TestGetKeycloakEndpointAdminDevModeOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/admin/realms/"+config.GetKeycloakRealm(), config.GetKeycloakEndpointAdmin)
}

func TestGetKeycloakEndpointAdminSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointAdminSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_ADMIN", config.GetKeycloakEndpointAdmin)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_ADMIN", config.GetKeycloakEndpointAdmin)
}

func TestGetKeycloakEndpointAuthzResourcesetDevModeOK(t *testing.T) {
Expand All @@ -105,9 +105,9 @@ func TestGetKeycloakEndpointAuthzResourcesetDevModeOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/authz/protection/resource_set", config.GetKeycloakEndpointAuthzResourceset)
}

func TestGetKeycloakEndpointAuthzResourcesetSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointAuthzResourcesetSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_AUTHZ_RESOURCESET", config.GetKeycloakEndpointAuthzResourceset)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_AUTHZ_RESOURCESET", config.GetKeycloakEndpointAuthzResourceset)
}

func TestGetKeycloakEndpointClientsDevModeOK(t *testing.T) {
Expand All @@ -116,9 +116,9 @@ func TestGetKeycloakEndpointClientsDevModeOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/admin/realms/"+config.GetKeycloakRealm()+"/clients", config.GetKeycloakEndpointClients)
}

func TestGetKeycloakEndpoinClientsSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpoinClientsSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_CLIENTS", config.GetKeycloakEndpointClients)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_CLIENTS", config.GetKeycloakEndpointClients)
}

func TestGetKeycloakEndpointAuthDevModeOK(t *testing.T) {
Expand All @@ -127,9 +127,9 @@ func TestGetKeycloakEndpointAuthDevModeOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/protocol/openid-connect/auth", config.GetKeycloakEndpointAuth)
}

func TestGetKeycloakEndpointAuthSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointAuthSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_AUTH", config.GetKeycloakEndpointAuth)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_AUTH", config.GetKeycloakEndpointAuth)
}

func TestGetKeycloakEndpointLogoutDevModeOK(t *testing.T) {
Expand All @@ -138,9 +138,9 @@ func TestGetKeycloakEndpointLogoutDevModeOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/protocol/openid-connect/logout", config.GetKeycloakEndpointLogout)
}

func TestGetKeycloakEndpointLogoutSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointLogoutSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_LOGOUT", config.GetKeycloakEndpointLogout)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_LOGOUT", config.GetKeycloakEndpointLogout)
}

func TestGetKeycloakEndpointTokenOK(t *testing.T) {
Expand All @@ -149,9 +149,9 @@ func TestGetKeycloakEndpointTokenOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/protocol/openid-connect/token", config.GetKeycloakEndpointToken)
}

func TestGetKeycloakEndpointTokenSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointTokenSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_TOKEN", config.GetKeycloakEndpointToken)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_TOKEN", config.GetKeycloakEndpointToken)
}

func TestGetKeycloakEndpointUserInfoOK(t *testing.T) {
Expand All @@ -168,12 +168,12 @@ func TestGetKeycloakEndpointLinkIDPOK(t *testing.T) {
expectedEndpoint := config.GetKeycloakDevModeURL() + "/auth/admin/realms/" + config.GetKeycloakRealm() + "/users/" + sampleID + "/federated-identity/" + idp
url, err := config.GetKeycloakEndpointLinkIDP(reqLong, sampleID, idp)
assert.Nil(t, err)
// In dev mode it's always the defualt value regardless of the request
// In dev mode it's always the default value regardless of the request
assert.Equal(t, expectedEndpoint, url)

url, err = config.GetKeycloakEndpointLinkIDP(reqShort, sampleID, idp)
assert.Nil(t, err)
// In dev mode it's always the defualt value regardless of the request
// In dev mode it's always the default value regardless of the request
assert.Equal(t, expectedEndpoint, url)
}

Expand All @@ -183,9 +183,9 @@ func TestGetKeycloakEndpointUsersOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/admin/realms/"+config.GetKeycloakRealm()+"/users", config.GetKeycloakEndpointUsers)
}

func TestGetKeycloakEndpointUserInfoSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointUserInfoSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_USERINFO", config.GetKeycloakEndpointUserInfo)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_USERINFO", config.GetKeycloakEndpointUserInfo)
}

func TestGetKeycloakEndpointEntitlementOK(t *testing.T) {
Expand All @@ -194,9 +194,9 @@ func TestGetKeycloakEndpointEntitlementOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/authz/entitlement/fabric8-online-platform", config.GetKeycloakEndpointEntitlement)
}

func TestGetKeycloakEndpointEntitlementSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointEntitlementSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_ENTITLEMENT", config.GetKeycloakEndpointEntitlement)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_ENTITLEMENT", config.GetKeycloakEndpointEntitlement)
}

func TestGetKeycloakEndpointBrokerOK(t *testing.T) {
Expand All @@ -205,9 +205,9 @@ func TestGetKeycloakEndpointBrokerOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/broker", config.GetKeycloakEndpointBroker)
}

func TestGetKeycloakEndpointBrokerSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakEndpointBrokerSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_BROKER", config.GetKeycloakEndpointBroker)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_BROKER", config.GetKeycloakEndpointBroker)
}

func TestGetKeycloakUserInfoEndpointOK(t *testing.T) {
Expand All @@ -216,9 +216,9 @@ func TestGetKeycloakUserInfoEndpointOK(t *testing.T) {
checkGetKeycloakEndpointOK(t, config.GetKeycloakDevModeURL()+"/auth/realms/"+config.GetKeycloakRealm()+"/account", config.GetKeycloakAccountEndpoint)
}

func TestGetKeycloakUserInfoEndpointOKrSetByEnvVaribaleOK(t *testing.T) {
func TestGetKeycloakUserInfoEndpointOKrSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
checkGetKeycloakEndpointSetByEnvVaribaleOK(t, "AUTH_KEYCLOAK_ENDPOINT_ACCOUNT", config.GetKeycloakAccountEndpoint)
checkGetKeycloakEndpointSetByEnvVariableOK(t, "AUTH_KEYCLOAK_ENDPOINT_ACCOUNT", config.GetKeycloakAccountEndpoint)
}

func TestGetWITURLNotDevModeOK(t *testing.T) {
Expand Down Expand Up @@ -285,12 +285,12 @@ func TestGetWITURLSetViaEnvVarOK(t *testing.T) {
func checkGetKeycloakEndpointOK(t *testing.T, expectedEndpoint string, getEndpoint func(req *goa.RequestData) (string, error)) {
url, err := getEndpoint(reqLong)
assert.Nil(t, err)
// In dev mode it's always the defualt value regardless of the request
// In dev mode it's always the default value regardless of the request
assert.Equal(t, expectedEndpoint, url)

url, err = getEndpoint(reqShort)
assert.Nil(t, err)
// In dev mode it's always the defualt value regardless of the request
// In dev mode it's always the default value regardless of the request
assert.Equal(t, expectedEndpoint, url)
}

Expand Down Expand Up @@ -324,7 +324,7 @@ func TestGetMaxHeaderSizeUsingDefaults(t *testing.T) {
assert.Equal(t, int64(5000), viperValue)
}

func TestGetMaxHeaderSizeSetByEnvVaribaleOK(t *testing.T) {
func TestGetMaxHeaderSizeSetByEnvVariableOK(t *testing.T) {
resource.Require(t, resource.UnitTest)
envName := "AUTH_HEADER_MAXLENGTH"
envValue := time.Now().Unix()
Expand Down Expand Up @@ -488,6 +488,33 @@ func checkCluster(t *testing.T, clusters map[string]configuration.OSOCluster, ex
require.Nil(t, err)
}

func TestExpiresIn(t *testing.T) {
checkExpiresIn(t, "AUTH_USERACCOUNT_TOKEN_ACCESS_EXPIRESIN", "too short lifespan of access tokens")
checkExpiresIn(t, "AUTH_USERACCOUNT_TOKEN_REFRESH_EXPIRESIN", "too short lifespan of refresh tokens")
}

func checkExpiresIn(t *testing.T, envVarName, expectedErrorMessage string) {
resource.Require(t, resource.UnitTest)

tokenExpiresIn := os.Getenv(envVarName)
defer func() {
os.Setenv(envVarName, tokenExpiresIn)
resetConfiguration()
}()

// There should be an error message if expiresIn is less than 3 minutes
os.Setenv(envVarName, "179")
resetConfiguration()

assert.Contains(t, config.DefaultConfigurationError().Error(), expectedErrorMessage)

// No error message if expiresIn is >= 3 minutes
os.Setenv(envVarName, "180")
resetConfiguration()

assert.NotContains(t, config.DefaultConfigurationError().Error(), expectedErrorMessage)
}

func TestIsTLSInsecureSkipVerifySetToFalse(t *testing.T) {
resource.Require(t, resource.UnitTest)
require.False(t, config.IsTLSInsecureSkipVerify())
Expand All @@ -497,7 +524,7 @@ func generateEnvKey(yamlKey string) string {
return "AUTH_" + strings.ToUpper(strings.Replace(yamlKey, ".", "_", -1))
}

func checkGetKeycloakEndpointSetByEnvVaribaleOK(t *testing.T, envName string, getEndpoint func(req *goa.RequestData) (string, error)) {
func checkGetKeycloakEndpointSetByEnvVariableOK(t *testing.T, envName string, getEndpoint func(req *goa.RequestData) (string, error)) {
envValue := uuid.NewV4().String()
env := os.Getenv(envName)
defer func() {
Expand Down
2 changes: 1 addition & 1 deletion controller/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (c *AuthorizeController) Authorize(ctx *app.AuthorizeAuthorizeContext) erro
ClientSecret: c.Configuration.GetKeycloakSecret(),
Scopes: scope,
Endpoint: oauth2.Endpoint{AuthURL: authEndpoint, TokenURL: tokenEndpoint},
RedirectURL: rest.AbsoluteURL(ctx.RequestData, client.CallbackAuthorizePath()),
RedirectURL: rest.AbsoluteURL(ctx.RequestData, client.CallbackAuthorizePath(), nil),
}

redirectTo, err := c.Auth.AuthCodeURL(ctx, &ctx.RedirectURI, ctx.APIClient, &ctx.State, ctx.ResponseMode, ctx.RequestData, oauth, c.Configuration)
Expand Down
2 changes: 1 addition & 1 deletion controller/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (c *LoginController) Login(ctx *app.LoginLoginContext) error {
ClientSecret: c.Configuration.GetKeycloakSecret(),
Scopes: []string{"user:email"},
Endpoint: oauth2.Endpoint{AuthURL: authEndpoint, TokenURL: tokenEndpoint},
RedirectURL: rest.AbsoluteURL(ctx.RequestData, "/api/login"),
RedirectURL: rest.AbsoluteURL(ctx.RequestData, "/api/login", nil),
}

ctx.ResponseData.Header().Set("Cache-Control", "no-cache")
Expand Down
12 changes: 6 additions & 6 deletions controller/openid_configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ func NewOpenidConfigurationController(service *goa.Service) *OpenidConfiguration
// Show runs the show action.
func (c *OpenidConfigurationController) Show(ctx *app.ShowOpenidConfigurationContext) error {

issuer := rest.AbsoluteURL(ctx.RequestData, "")
authorizationEndpoint := rest.AbsoluteURL(ctx.RequestData, client.AuthorizeAuthorizePath())
tokenEndpoint := rest.AbsoluteURL(ctx.RequestData, client.ExchangeTokenPath())
userinfoEndpoint := rest.AbsoluteURL(ctx.RequestData, client.ShowUserinfoPath())
logoutEndpoint := rest.AbsoluteURL(ctx.RequestData, client.LogoutLogoutPath())
jwksURI := rest.AbsoluteURL(ctx.RequestData, client.KeysTokenPath())
issuer := rest.AbsoluteURL(ctx.RequestData, "", nil)
authorizationEndpoint := rest.AbsoluteURL(ctx.RequestData, client.AuthorizeAuthorizePath(), nil)
tokenEndpoint := rest.AbsoluteURL(ctx.RequestData, client.ExchangeTokenPath(), nil)
userinfoEndpoint := rest.AbsoluteURL(ctx.RequestData, client.ShowUserinfoPath(), nil)
logoutEndpoint := rest.AbsoluteURL(ctx.RequestData, client.LogoutLogoutPath(), nil)
jwksURI := rest.AbsoluteURL(ctx.RequestData, client.KeysTokenPath(), nil)

authOpenIDConfiguration := &app.OpenIDConfiguration{
// REQUIRED properties
Expand Down
2 changes: 1 addition & 1 deletion controller/paging.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func setPagingLinks(links *app.PagingLinks, path string, resultLen, offset, limi
}

func buildAbsoluteURL(req *goa.RequestData) string {
return rest.AbsoluteURL(req, req.URL.Path)
return rest.AbsoluteURL(req, req.URL.Path, nil)
}

func parseInts(s *string) ([]int, error) {
Expand Down
4 changes: 2 additions & 2 deletions controller/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
)

const (
expectedDefaultConfDevModeErrorMessage = "Error: /etc/fabric8/service-account-secrets.conf is not used; /etc/fabric8/oso-clusters.conf is not used; developer Mode is enabled; default service account private key is used; default service account private key ID is used; default DB password is used; default Keycloak client secret is used; default GitHub client secret is used; no restrictions for valid redirect URLs; notification service url is empty"
expectedDefaultConfProdModeErrorMessage = "Error: /etc/fabric8/service-account-secrets.conf is not used; /etc/fabric8/oso-clusters.conf is not used; default service account private key is used; default service account private key ID is used; default DB password is used; default Keycloak client secret is used; default GitHub client secret is used; notification service url is empty"
expectedDefaultConfDevModeErrorMessage = "Error: /etc/fabric8/service-account-secrets.conf is not used; /etc/fabric8/oso-clusters.conf is not used; developer Mode is enabled; default service account private key is used; default service account private key ID is used; default user account private key is used; default user account private key ID is used; default DB password is used; default Keycloak client secret is used; default GitHub client secret is used; no restrictions for valid redirect URLs; notification service url is empty"
expectedDefaultConfProdModeErrorMessage = "Error: /etc/fabric8/service-account-secrets.conf is not used; /etc/fabric8/oso-clusters.conf is not used; default service account private key is used; default service account private key ID is used; default user account private key is used; default user account private key ID is used; default DB password is used; default Keycloak client secret is used; default GitHub client secret is used; notification service url is empty"
)

type TestStatusREST struct {
Expand Down
12 changes: 10 additions & 2 deletions controller/test-files/token/keys/ok_jwk.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
{
"alg": "RS256",
"e": "AQAB",
"kid": "bNq-BCOR3ev-E6buGSaPrU-0SXX8whhDlmZ6geenkTE",
"kid": "aUGv8mQA85jg4V1DU8Uk1W0uKsxn187KQONAGl6AMtc",
"kty": "RSA",
"n": "vQ8p-HsTMrgcsuIMoOR1LXRhynL9YAU0qoDON6PLKCpdBv0Xy_jnsPjo5DrtUOijuJcID8CR7E0hYpY9MgK5H5pDFwC4lbUVENquHEVS_E0pQSKCIzSmORcIhjYW2-wKfDOVjeudZwdFBIxJ6KpIty_aF78hlUJZuvghFVqoHQYTq_DZOmKjS-PAVLw8FKE3wa_3WU0EkpP-iovRMCkllzxqrcLPIvx-T2gkwe0bn0kTvdMOhTLTN2tuvKrFpVUxVi8RM_V8PtgdKroxnES7SyUqK8rLO830jKJzAYrByQL-sdGuSqInIY_geahQHEGTwMI0CLj6zfhpjSgCflstvw",
"n": "40yB6SNoU4SpWxTfG5ilu-BlLYikRyyEcJIGg__w_GyqtjvT_CVo92DRTh_DlrgwjSitmZrhauBnrCOoUBMin0_TXeSo3w2M5tEiiIFPbTDRf2jMfbSGEOke9O0USCCR-bM2TncrgZR74qlSwq38VCND4zHc89rAzqJ2LVM2aXkuBbO7TcgLNyooBrpOK9khVHAD64cyODAdJY4esUjcLdlcB7TMDGOgxGGn2RARU7-TUf32gZZbTMikbuPM5gXuzGlo_22ECbQSKuZpbGwgPIAZ5NN9QA4D1NRz9-KDoiXZ6deZTTVCrZykJJ6RyLNfRh-XS-6G5nvcqAmfBpyOWw",
"use": "sig"
},
{
Expand All @@ -15,6 +15,14 @@
"kty": "RSA",
"n": "nwrjH5iTSErw9xUptp6QSFoUfpHUXZ-PaslYSUrpLjw1q27ODSFwmhV4-dAaTMO5chFv_kM36H3ZOyA146nwxBobS723okFaIkshRrf6qgtD6coTHlVUSBTAcwKEjNn4C9jtEpyOl-eSgxhMzRH3bwTIFlLlVMiZf7XVE7P3yuOCpqkk2rdYVSpQWQWKU-ZRywJkYcLwjEYjc70AoNpjO5QnY-Exx98E30iEdPHZpsfNhsjh9Z7IX5TrMYgz7zBTw8-niO_uq3RBaHyIhDbvenbR9Q59d88lbnEeHKgSMe2RQpFR3rxFRkc_64Rn_bMuL_ptNowPqh1P-9GjYzWmPw",
"use": "sig"
},
{
"alg": "RS256",
"e": "AQAB",
"kid": "bNq-BCOR3ev-E6buGSaPrU-0SXX8whhDlmZ6geenkTE",
"kty": "RSA",
"n": "vQ8p-HsTMrgcsuIMoOR1LXRhynL9YAU0qoDON6PLKCpdBv0Xy_jnsPjo5DrtUOijuJcID8CR7E0hYpY9MgK5H5pDFwC4lbUVENquHEVS_E0pQSKCIzSmORcIhjYW2-wKfDOVjeudZwdFBIxJ6KpIty_aF78hlUJZuvghFVqoHQYTq_DZOmKjS-PAVLw8FKE3wa_3WU0EkpP-iovRMCkllzxqrcLPIvx-T2gkwe0bn0kTvdMOhTLTN2tuvKrFpVUxVi8RM_V8PtgdKroxnES7SyUqK8rLO830jKJzAYrByQL-sdGuSqInIY_geahQHEGTwMI0CLj6zfhpjSgCflstvw",
"use": "sig"
}
]
}
Loading

0 comments on commit d39e42a

Please sign in to comment.