package osin import ( "net/http" "net/url" "testing" "time" ) func TestAccessAuthorizationCode(t *testing.T) { sconfig := NewServerConfig() sconfig.AllowedAccessTypes = AllowedAccessType{AUTHORIZATION_CODE} server := NewServer(sconfig, NewTestingStorage()) server.AccessTokenGen = &TestingAccessTokenGen{} resp := server.NewResponse() req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("1234", "aabbccdd") req.Form = make(url.Values) req.Form.Set("grant_type", string(AUTHORIZATION_CODE)) req.Form.Set("code", "9999") req.Form.Set("state", "a") req.PostForm = make(url.Values) if ar := server.HandleAccessRequest(resp, req); ar != nil { ar.Authorized = true server.FinishAccessRequest(resp, req, ar) } //fmt.Printf("%+v", resp) if resp.IsError && resp.InternalError != nil { t.Fatalf("Error in response: %s", resp.InternalError) } if resp.IsError { t.Fatalf("Should not be an error") } if resp.Type != DATA { t.Fatalf("Response should be data") } if d := resp.Output["access_token"]; d != "1" { t.Fatalf("Unexpected access token: %s", d) } if d := resp.Output["refresh_token"]; d != "r1" { t.Fatalf("Unexpected refresh token: %s", d) } } func TestAccessRefreshToken(t *testing.T) { sconfig := NewServerConfig() sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN} server := NewServer(sconfig, NewTestingStorage()) server.AccessTokenGen = &TestingAccessTokenGen{} resp := server.NewResponse() req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("1234", "aabbccdd") req.Form = make(url.Values) req.Form.Set("grant_type", string(REFRESH_TOKEN)) req.Form.Set("refresh_token", "r9999") req.Form.Set("state", "a") req.PostForm = make(url.Values) if ar := server.HandleAccessRequest(resp, req); ar != nil { ar.Authorized = true server.FinishAccessRequest(resp, req, ar) } //fmt.Printf("%+v", resp) if _, err := server.Storage.LoadRefresh("r9999"); err == nil { t.Fatalf("token was not deleted") } if resp.IsError && resp.InternalError != nil { t.Fatalf("Error in response: %s", resp.InternalError) } if resp.IsError { t.Fatalf("Should not be an error") } if resp.Type != DATA { t.Fatalf("Response should be data") } if d := resp.Output["access_token"]; d != "1" { t.Fatalf("Unexpected access token: %s", d) } if d := resp.Output["refresh_token"]; d != "r1" { t.Fatalf("Unexpected refresh token: %s", d) } } func TestAccessRefreshTokenSaveToken(t *testing.T) { sconfig := NewServerConfig() sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN} server := NewServer(sconfig, NewTestingStorage()) server.AccessTokenGen = &TestingAccessTokenGen{} server.Config.RetainTokenAfterRefresh = true resp := server.NewResponse() req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("1234", "aabbccdd") req.Form = make(url.Values) req.Form.Set("grant_type", string(REFRESH_TOKEN)) req.Form.Set("refresh_token", "r9999") req.Form.Set("state", "a") req.PostForm = make(url.Values) if ar := server.HandleAccessRequest(resp, req); ar != nil { ar.Authorized = true server.FinishAccessRequest(resp, req, ar) } //fmt.Printf("%+v", resp) if _, err := server.Storage.LoadRefresh("r9999"); err != nil { t.Fatalf("token incorrectly deleted: %s", err.Error()) } if resp.IsError && resp.InternalError != nil { t.Fatalf("Error in response: %s", resp.InternalError) } if resp.IsError { t.Fatalf("Should not be an error") } if resp.Type != DATA { t.Fatalf("Response should be data") } if d := resp.Output["access_token"]; d != "1" { t.Fatalf("Unexpected access token: %s", d) } if d := resp.Output["refresh_token"]; d != "r1" { t.Fatalf("Unexpected refresh token: %s", d) } } func TestAccessPassword(t *testing.T) { sconfig := NewServerConfig() sconfig.AllowedAccessTypes = AllowedAccessType{PASSWORD} server := NewServer(sconfig, NewTestingStorage()) server.AccessTokenGen = &TestingAccessTokenGen{} resp := server.NewResponse() req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("1234", "aabbccdd") req.Form = make(url.Values) req.Form.Set("grant_type", string(PASSWORD)) req.Form.Set("username", "testing") req.Form.Set("password", "testing") req.Form.Set("state", "a") req.PostForm = make(url.Values) if ar := server.HandleAccessRequest(resp, req); ar != nil { ar.Authorized = ar.Username == "testing" && ar.Password == "testing" server.FinishAccessRequest(resp, req, ar) } //fmt.Printf("%+v", resp) if resp.IsError && resp.InternalError != nil { t.Fatalf("Error in response: %s", resp.InternalError) } if resp.IsError { t.Fatalf("Should not be an error") } if resp.Type != DATA { t.Fatalf("Response should be data") } if d := resp.Output["access_token"]; d != "1" { t.Fatalf("Unexpected access token: %s", d) } if d := resp.Output["refresh_token"]; d != "r1" { t.Fatalf("Unexpected refresh token: %s", d) } } func TestAccessClientCredentials(t *testing.T) { sconfig := NewServerConfig() sconfig.AllowedAccessTypes = AllowedAccessType{CLIENT_CREDENTIALS} server := NewServer(sconfig, NewTestingStorage()) server.AccessTokenGen = &TestingAccessTokenGen{} resp := server.NewResponse() req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("1234", "aabbccdd") req.Form = make(url.Values) req.Form.Set("grant_type", string(CLIENT_CREDENTIALS)) req.Form.Set("state", "a") req.PostForm = make(url.Values) if ar := server.HandleAccessRequest(resp, req); ar != nil { ar.Authorized = true server.FinishAccessRequest(resp, req, ar) } //fmt.Printf("%+v", resp) if resp.IsError && resp.InternalError != nil { t.Fatalf("Error in response: %s", resp.InternalError) } if resp.IsError { t.Fatalf("Should not be an error") } if resp.Type != DATA { t.Fatalf("Response should be data") } if d := resp.Output["access_token"]; d != "1" { t.Fatalf("Unexpected access token: %s", d) } if d, dok := resp.Output["refresh_token"]; dok { t.Fatalf("Refresh token should not be generated: %s", d) } } func TestExtraScopes(t *testing.T) { if extraScopes("", "") == true { t.Fatalf("extraScopes returned true with empty scopes") } if extraScopes("a", "") == true { t.Fatalf("extraScopes returned true with less scopes") } if extraScopes("a,b", "b,a") == true { t.Fatalf("extraScopes returned true with matching scopes") } if extraScopes("a,b", "b,a,c") == false { t.Fatalf("extraScopes returned false with extra scopes") } if extraScopes("", "a") == false { t.Fatalf("extraScopes returned false with extra scopes") } } // clientWithoutMatcher just implements the base Client interface type clientWithoutMatcher struct { Id string Secret string RedirectUri string } func (c *clientWithoutMatcher) GetId() string { return c.Id } func (c *clientWithoutMatcher) GetSecret() string { return c.Secret } func (c *clientWithoutMatcher) GetRedirectUri() string { return c.RedirectUri } func (c *clientWithoutMatcher) GetUserData() interface{} { return nil } func TestGetClientWithoutMatcher(t *testing.T) { myclient := &clientWithoutMatcher{ Id: "myclient", Secret: "myclientsecret", RedirectUri: "http://www.example.com", } storage := &TestingStorage{clients: map[string]Client{myclient.Id: myclient}} // Ensure bad secret fails { auth := &BasicAuth{ Username: "myclient", Password: "invalidsecret", } w := &Response{} client := getClient(auth, storage, w) if client != nil { t.Errorf("Expected error, got client: %v", client) } if !w.IsError { t.Error("No error in response") } if w.ErrorId != E_UNAUTHORIZED_CLIENT { t.Errorf("Expected error %v, got %v", E_UNAUTHORIZED_CLIENT, w.ErrorId) } } // Ensure nonexistent client fails { auth := &BasicAuth{ Username: "nonexistent", Password: "nonexistent", } w := &Response{} client := getClient(auth, storage, w) if client != nil { t.Errorf("Expected error, got client: %v", client) } if !w.IsError { t.Error("No error in response") } if w.ErrorId != E_UNAUTHORIZED_CLIENT { t.Errorf("Expected error %v, got %v", E_UNAUTHORIZED_CLIENT, w.ErrorId) } } // Ensure good secret works { auth := &BasicAuth{ Username: "myclient", Password: "myclientsecret", } w := &Response{} client := getClient(auth, storage, w) if client != myclient { t.Errorf("Expected client, got nil with response: %v", w) } } } // clientWithMatcher implements the base Client interface and the ClientSecretMatcher interface type clientWithMatcher struct { Id string Secret string RedirectUri string } func (c *clientWithMatcher) GetId() string { return c.Id } func (c *clientWithMatcher) GetSecret() string { panic("called GetSecret"); return "" } func (c *clientWithMatcher) GetRedirectUri() string { return c.RedirectUri } func (c *clientWithMatcher) GetUserData() interface{} { return nil } func (c *clientWithMatcher) ClientSecretMatches(secret string) bool { return secret == c.Secret } func TestGetClientSecretMatcher(t *testing.T) { myclient := &clientWithMatcher{ Id: "myclient", Secret: "myclientsecret", RedirectUri: "http://www.example.com", } storage := &TestingStorage{clients: map[string]Client{myclient.Id: myclient}} // Ensure bad secret fails, but does not panic (doesn't call GetSecret) { auth := &BasicAuth{ Username: "myclient", Password: "invalidsecret", } w := &Response{} client := getClient(auth, storage, w) if client != nil { t.Errorf("Expected error, got client: %v", client) } } // Ensure good secret works, but does not panic (doesn't call GetSecret) { auth := &BasicAuth{ Username: "myclient", Password: "myclientsecret", } w := &Response{} client := getClient(auth, storage, w) if client != myclient { t.Errorf("Expected client, got nil with response: %v", w) } } } func TestAccessAuthorizationCodePKCE(t *testing.T) { testcases := map[string]struct { Challenge string ChallengeMethod string Verifier string ExpectedError string }{ "good, plain": { Challenge: "12345678901234567890123456789012345678901234567890", Verifier: "12345678901234567890123456789012345678901234567890", }, "bad, plain": { Challenge: "12345678901234567890123456789012345678901234567890", Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ExpectedError: "invalid_grant", }, "good, S256": { Challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", ChallengeMethod: "S256", Verifier: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk", }, "bad, S256": { Challenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", ChallengeMethod: "S256", Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ExpectedError: "invalid_grant", }, "missing from storage": { Challenge: "", ChallengeMethod: "", Verifier: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", }, } for k, test := range testcases { testStorage := NewTestingStorage() sconfig := NewServerConfig() sconfig.AllowedAccessTypes = AllowedAccessType{AUTHORIZATION_CODE} server := NewServer(sconfig, testStorage) server.AccessTokenGen = &TestingAccessTokenGen{} server.Storage.SaveAuthorize(&AuthorizeData{ Client: testStorage.clients["public-client"], Code: "pkce-code", ExpiresIn: 3600, CreatedAt: time.Now(), RedirectUri: "http://localhost:14000/appauth", CodeChallenge: test.Challenge, CodeChallengeMethod: test.ChallengeMethod, }) resp := server.NewResponse() req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) if err != nil { t.Fatal(err) } req.SetBasicAuth("public-client", "") req.Form = make(url.Values) req.Form.Set("grant_type", string(AUTHORIZATION_CODE)) req.Form.Set("code", "pkce-code") req.Form.Set("state", "a") req.Form.Set("code_verifier", test.Verifier) req.PostForm = make(url.Values) if ar := server.HandleAccessRequest(resp, req); ar != nil { ar.Authorized = true server.FinishAccessRequest(resp, req, ar) } if resp.IsError { if test.ExpectedError == "" || test.ExpectedError != resp.ErrorId { t.Errorf("%s: unexpected error: %v, %v", k, resp.ErrorId, resp.StatusText) continue } } if test.ExpectedError == "" { if resp.Type != DATA { t.Fatalf("%s: Response should be data", k) } if d := resp.Output["access_token"]; d != "1" { t.Fatalf("%s: Unexpected access token: %s", k, d) } if d := resp.Output["refresh_token"]; d != "r1" { t.Fatalf("%s: Unexpected refresh token: %s", k, d) } } } }