func subjectFromBearerToken(core *roll.Core, r *http.Request) (string, error) { //Check for header presence authzHeader := r.Header.Get("Authorization") if authzHeader == "" { return "", errors.New("Authorization header missing from request") } //Header format should be Bearer token parts := strings.SplitAfter(authzHeader, "Bearer") if len(parts) != 2 { return "", errors.New("Unexpected authorization header format - expecting bearer token") } //Parse the token bearerToken := strings.TrimSpace(parts[1]) token, err := jwt.Parse(bearerToken, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) if err != nil { return "", err } //Grab the subject from the claims subject, ok := token.Claims["sub"].(string) if !ok { return "", errors.New("problem with subject claim") } //Is the subject something other than an empty string? if subject == "" { return "", errors.New("empty subject claim") } return subject, nil }
func TestAuthValidateCodeResponseAuthenticateOk(t *testing.T) { core, coreConfig := NewTestCore() ln, addr := TestServer(t, core) defer ln.Close() privateKey, publicKey, err := secrets.GenerateKeyPair() assert.Nil(t, err) secretsMock := coreConfig.SecretsRepo.(*mocks.SecretsRepo) secretsMock.On("RetrievePrivateKeyForApp", "1111-2222-3333333-4444444").Return(privateKey, nil) secretsMock.On("RetrievePublicKeyForApp", "1111-2222-3333333-4444444").Return(publicKey, nil) var loginCalled = false ls := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { loginCalled = true w.WriteHeader(http.StatusOK) })) defer ls.Close() //TODO - use a second callback where we serve up a script to extract the page details sent //on deny and post those details to another test server. var callbackInvoked = false ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callbackInvoked = true code := r.FormValue("code") token, err := jwt.Parse(code, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) assert.Nil(t, err) scope, ok := token.Claims["scope"].(string) assert.True(t, ok) assert.Equal(t, "xtAuthCode", scope) })) defer ts.Close() lsURL, _ := url.Parse(ls.URL) returnVal := roll.Application{ DeveloperEmail: "*****@*****.**", ClientID: "1111-2222-3333333-4444444", ApplicationName: "fight club", ClientSecret: "not for browser clients", RedirectURI: ts.URL, LoginProvider: "xtrac://" + lsURL.Host, } appRepoMock := coreConfig.ApplicationRepo.(*mocks.ApplicationRepo) appRepoMock.On("SystemRetrieveApplication", "1111-2222-3333333-4444444").Return(&returnVal, nil) _, err = http.PostForm(addr+"/oauth2/validate", url.Values{"username": {"x"}, "password": {"y"}, "authorize": {"allow"}, "response_type": {"code"}, "client_id": {"1111-2222-3333333-4444444"}}) assert.Nil(t, err) assert.True(t, callbackInvoked) assert.True(t, loginCalled) }
func TestPWGrantLoginOk(t *testing.T) { core, coreConfig := NewTestCore() ln, addr := TestServer(t, core) defer ln.Close() var loginCalled = false ls := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { loginCalled = true w.WriteHeader(http.StatusOK) })) defer ls.Close() lsURL, _ := url.Parse(ls.URL) returnVal := roll.Application{ DeveloperEmail: "*****@*****.**", ClientID: "1111-2222-3333333-4444444", ApplicationName: "fight club", ClientSecret: "not for browser clients", RedirectURI: "http://localhost:3000/ab", LoginProvider: "xtrac://" + lsURL.Host, } appRepoMock := coreConfig.ApplicationRepo.(*mocks.ApplicationRepo) appRepoMock.On("SystemRetrieveApplication", "1111-2222-3333333-4444444").Return(&returnVal, nil) privateKey, publicKey, err := secrets.GenerateKeyPair() assert.Nil(t, err) secretsMock := coreConfig.SecretsRepo.(*mocks.SecretsRepo) secretsMock.On("RetrievePrivateKeyForApp", "1111-2222-3333333-4444444").Return(privateKey, nil) secretsMock.On("RetrievePublicKeyForApp", "1111-2222-3333333-4444444").Return(publicKey, nil) resp, err := http.PostForm(addr+OAuth2TokenBaseURI, url.Values{"grant_type": {"password"}, "client_id": {"1111-2222-3333333-4444444"}, "client_secret": {"not for browser clients"}, "username": {"abc"}, "password": {"xxxxxxxx"}}) assert.True(t, loginCalled) assert.Nil(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) body := responseAsString(t, resp) var jsonResponse accessTokenResponse err = json.Unmarshal([]byte(body), &jsonResponse) assert.Nil(t, err) assert.True(t, jsonResponse.AccessToken != "") assert.True(t, jsonResponse.TokenType == "Bearer") token, err := jwt.Parse(jsonResponse.AccessToken, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) assert.Nil(t, err) fmt.Println(token.Claims) assert.Equal(t, "1111-2222-3333333-4444444", token.Claims["aud"].(string)) assert.Equal(t, "abc", token.Claims["sub"].(string)) assert.Equal(t, "", token.Claims["scope"].(string)) }
func TestTokenValidCodeWithAdminScope(t *testing.T) { core, coreConfig := NewTestCore() ln, addr := TestServer(t, core) defer ln.Close() returnVal := roll.Application{ DeveloperEmail: "*****@*****.**", ClientID: "1111-2222-3333333-4444444", ApplicationName: "fight club", ClientSecret: "not for browser clients", RedirectURI: "http://localhost:3000/ab", LoginProvider: "xtrac://localhost:9000", } appRepoMock := coreConfig.ApplicationRepo.(*mocks.ApplicationRepo) appRepoMock.On("SystemRetrieveApplication", "1111-2222-3333333-4444444").Return(&returnVal, nil) privateKey, publicKey, err := secrets.GenerateKeyPair() assert.Nil(t, err) secretsMock := coreConfig.SecretsRepo.(*mocks.SecretsRepo) secretsMock.On("RetrievePrivateKeyForApp", "1111-2222-3333333-4444444").Return(privateKey, nil) secretsMock.On("RetrievePublicKeyForApp", "1111-2222-3333333-4444444").Return(publicKey, nil) code, err := rolltoken.GenerateCode("b-subject", "admin", returnVal.ClientID, privateKey) assert.Nil(t, err) resp, err := http.PostForm(addr+OAuth2TokenBaseURI, url.Values{"grant_type": {"authorization_code"}, "client_id": {"1111-2222-3333333-4444444"}, "client_secret": {"not for browser clients"}, "redirect_uri": {"http://localhost:3000/ab"}, "code": {code}}) assert.Nil(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) body := responseAsString(t, resp) var jsonResponse accessTokenResponse err = json.Unmarshal([]byte(body), &jsonResponse) assert.Nil(t, err) assert.True(t, jsonResponse.AccessToken != "") assert.True(t, jsonResponse.TokenType == "Bearer") token, err := jwt.Parse(jsonResponse.AccessToken, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) assert.Nil(t, err) scope, ok := token.Claims["scope"].(string) assert.True(t, ok) assert.Equal(t, "admin", scope) }
func TestJWTFlowValidAssertionOkYeah(t *testing.T) { core, coreConfig := NewTestCore() ln, addr := TestServer(t, core) defer ln.Close() returnVal := roll.Application{ DeveloperEmail: "*****@*****.**", ClientID: "1111-2222-3333333-4444444", ApplicationName: "fight club", ClientSecret: "not for browser clients", RedirectURI: "http://localhost:3000/ab", LoginProvider: "xtrac://localhost:9000", JWTFlowPublicKey: publicKey, JWTFlowIssuer: "1111-2222-3333333-4444444", JWTFlowAudience: "captive", } appRepoMock := coreConfig.ApplicationRepo.(*mocks.ApplicationRepo) appRepoMock.On("SystemRetrieveApplicationByJWTFlowAudience", "captive").Return(&returnVal, nil) privateKey, publicKey, err := secrets.GenerateKeyPair() assert.Nil(t, err) secretsMock := coreConfig.SecretsRepo.(*mocks.SecretsRepo) secretsMock.On("RetrievePrivateKeyForApp", "1111-2222-3333333-4444444").Return(privateKey, nil) secretsMock.On("RetrievePublicKeyForApp", "1111-2222-3333333-4444444").Return(publicKey, nil) resp, err := http.PostForm(addr+OAuth2TokenBaseURI, url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {jwtAssertion}}) assert.Nil(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) bodyStr := responseAsString(t, resp) println(bodyStr) var jsonResponse accessTokenResponse err = json.Unmarshal([]byte(bodyStr), &jsonResponse) assert.Nil(t, err) assert.True(t, jsonResponse.AccessToken != "") assert.True(t, jsonResponse.TokenType == "Bearer") token, err := jwt.Parse(jsonResponse.AccessToken, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) assert.Nil(t, err) assert.Equal(t, "1111-2222-3333333-4444444", token.Claims["aud"].(string)) assert.Equal(t, "foo", token.Claims["sub"].(string)) assert.Equal(t, "", token.Claims["scope"].(string)) }
func validateAndReturnCodeToken(secretsRepo secrets.SecretsRepo, ctx *authCodeContext, clientID string) (*jwt.Token, error) { token, err := jwt.Parse(ctx.authCode, rolltoken.GenerateKeyExtractionFunction(secretsRepo)) if err != nil { return nil, err } //Make sure the token is valid if !token.Valid { log.Info("Invalid token presented to service: ", token) return nil, errors.New("Invalid authorization code") } //make sure the client_id used to validate the token matches the token aud claim if clientID != token.Claims["aud"] { return nil, errors.New("Token not associated with client id") } return token, nil }
func handleTokenInfoGet(core *roll.Core, w http.ResponseWriter, r *http.Request) { //Grab the token tokenString := r.FormValue("access_token") if tokenString == "" { log.Info("missing access token") w.WriteHeader(http.StatusBadRequest) return } //Parse and validate the token token, err := jwt.Parse(tokenString, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) if err != nil { log.Info(err.Error()) w.WriteHeader(http.StatusBadRequest) return } //Panic is there's no aud claim audience := token.Claims["aud"] if audience == "" { panic(errors.New("No aud claim in token")) } //Return the token info tokenInfo := &tokenInfo{ Audience: audience.(string), } bytes, err := json.Marshal(&tokenInfo) if err != nil { respondError(w, http.StatusInternalServerError, err) return } w.Write(bytes) }
func TestImplGrantAuthValidateAuthenticateOkAdminScope(t *testing.T) { var loginCalled = false ls := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { loginCalled = true w.WriteHeader(http.StatusOK) })) defer ls.Close() //TODO - use a second callback where we serve up a script to extract the page details sent //on deny and post those details to another test server. var callbackInvoked = false ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { callbackInvoked = true println(r.URL.RawQuery) })) defer ts.Close() core, coreConfig := NewTestCore() ln, addr := TestServer(t, core) defer ln.Close() lsURL, _ := url.Parse(ls.URL) returnVal := roll.Application{ DeveloperEmail: "*****@*****.**", ClientID: "1111-2222-3333333-4444444", ApplicationName: "fight club", ClientSecret: "not for browser clients", RedirectURI: ts.URL + "/foo", LoginProvider: "xtrac://" + lsURL.Host, } appRepoMock := coreConfig.ApplicationRepo.(*mocks.ApplicationRepo) appRepoMock.On("SystemRetrieveApplication", "1111-2222-3333333-4444444").Return(&returnVal, nil) privateKey, pk, err := secrets.GenerateKeyPair() assert.Nil(t, err) secretsMock := coreConfig.SecretsRepo.(*mocks.SecretsRepo) secretsMock.On("RetrievePrivateKeyForApp", "1111-2222-3333333-4444444").Return(privateKey, nil) secretsMock.On("RetrievePublicKeyForApp", "1111-2222-3333333-4444444").Return(pk, nil) adminRepoMock := coreConfig.AdminRepo.(*mocks.AdminRepo) adminRepoMock.On("IsAdmin", "x").Return(true, nil) client := &http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { fmt.Println(req.URL.Fragment) m, _ := url.ParseQuery(req.URL.Fragment) accessToken := m.Get("access_token") token, err := jwt.Parse(accessToken, rolltoken.GenerateKeyExtractionFunction(core.SecretsRepo)) assert.Nil(t, err) scope, ok := token.Claims["scope"].(string) assert.True(t, ok) assert.Equal(t, "admin", scope) assert.Equal(t, "x", token.Claims["sub"].(string)) assert.Equal(t, "1111-2222-3333333-4444444", token.Claims["aud"].(string)) assert.Equal(t, "Bearer", m.Get("token_type")) return nil }, } _, err = client.PostForm(addr+"/oauth2/validate", url.Values{"username": {"x"}, "password": {"y"}, "authorize": {"allow"}, "response_type": {"token"}, "scope": {"admin"}, "client_id": {"1111-2222-3333333-4444444"}}) assert.Nil(t, err) assert.True(t, callbackInvoked) assert.True(t, loginCalled) }