oauth_test.go (6731B)
1 package oauth2 2 3 import ( 4 "fmt" 5 . "github.com/stretchr/testify/assert" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "net/url" 10 "strings" 11 "testing" 12 ) 13 14 var testConfig = Config{ 15 ClientID: "client42", 16 ClientSecret: "secret", 17 AuthURL: "http://auth-provider/auth", 18 TokenURL: "http://auth-provider/token", 19 RedirectURI: "http://localhost/callback", 20 Scope: "email other", 21 } 22 23 func Test_StartFlow(t *testing.T) { 24 resp := httptest.NewRecorder() 25 StartFlow(testConfig, resp) 26 27 Equal(t, http.StatusFound, resp.Code) 28 29 // assert that we received a state cookie 30 cHeader := strings.Split(resp.Header().Get("Set-Cookie"), ";")[0] 31 Equal(t, stateCookieName, strings.Split(cHeader, "=")[0]) 32 state := strings.Split(cHeader, "=")[1] 33 34 expectedLocation := fmt.Sprintf("%v?client_id=%v&redirect_uri=%v&response_type=code&scope=%v&state=%v", 35 testConfig.AuthURL, 36 testConfig.ClientID, 37 url.QueryEscape(testConfig.RedirectURI), 38 "email+other", 39 state, 40 ) 41 42 Equal(t, expectedLocation, resp.Header().Get("Location")) 43 } 44 45 func Test_Authenticate(t *testing.T) { 46 // mock a server for token exchange 47 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 48 Equal(t, "POST", r.Method) 49 Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type")) 50 Equal(t, "application/json", r.Header.Get("Accept")) 51 52 body, _ := ioutil.ReadAll(r.Body) 53 Equal(t, "client_id=client42&client_secret=secret&code=theCode&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%2Fcallback", string(body)) 54 55 w.Header().Set("Content-Type", "application/json") 56 w.Write([]byte(`{"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"repo gist", "token_type":"bearer"}`)) 57 })) 58 defer server.Close() 59 60 testConfigCopy := testConfig 61 testConfigCopy.TokenURL = server.URL 62 63 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 64 request.Header.Set("Cookie", "oauthState=theState") 65 request.URL, _ = url.Parse("http://localhost/callback?code=theCode&state=theState") 66 67 tokenInfo, err := Authenticate(testConfigCopy, request) 68 69 NoError(t, err) 70 Equal(t, "e72e16c7e42f292c6912e7710c838347ae178b4a", tokenInfo.AccessToken) 71 Equal(t, "repo gist", tokenInfo.Scope) 72 Equal(t, "bearer", tokenInfo.TokenType) 73 } 74 75 func Test_Authenticate_CodeExchangeError(t *testing.T) { 76 var testReturnCode int 77 testResponseJSON := `{"error":"bad_verification_code","error_description":"The code passed is incorrect or expired.","error_uri":"https://developer.github.com/v3/oauth/#bad-verification-code"}` 78 // mock a server for token exchange 79 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 80 w.WriteHeader(testReturnCode) 81 w.Header().Set("Content-Type", "application/json") 82 w.Write([]byte(testResponseJSON)) 83 })) 84 defer server.Close() 85 86 testConfigCopy := testConfig 87 testConfigCopy.TokenURL = server.URL 88 89 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 90 request.Header.Set("Cookie", "oauthState=theState") 91 request.URL, _ = url.Parse("http://localhost/callback?code=theCode&state=theState") 92 93 testReturnCode = 500 94 tokenInfo, err := Authenticate(testConfigCopy, request) 95 Error(t, err) 96 EqualError(t, err, "error: expected http status 200 on token exchange, but got 500") 97 Equal(t, "", tokenInfo.AccessToken) 98 99 testReturnCode = 200 100 tokenInfo, err = Authenticate(testConfigCopy, request) 101 Error(t, err) 102 EqualError(t, err, `error: got "bad_verification_code" on token exchange`) 103 Equal(t, "", tokenInfo.AccessToken) 104 105 testReturnCode = 200 106 testResponseJSON = `{"foo": "bar"}` 107 tokenInfo, err = Authenticate(testConfigCopy, request) 108 Error(t, err) 109 EqualError(t, err, `error: no access_token on token exchange`) 110 Equal(t, "", tokenInfo.AccessToken) 111 112 } 113 114 func Test_Authentication_ProviderError(t *testing.T) { 115 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 116 request.URL, _ = url.Parse("http://localhost/callback?error=provider_login_error") 117 118 _, err := Authenticate(testConfig, request) 119 120 Error(t, err) 121 Equal(t, "error: provider_login_error", err.Error()) 122 } 123 124 func Test_Authentication_StateError(t *testing.T) { 125 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 126 request.Header.Set("Cookie", "oauthState=XXXXXXX") 127 request.URL, _ = url.Parse("http://localhost/callback?code=theCode&state=theState") 128 129 _, err := Authenticate(testConfig, request) 130 131 Error(t, err) 132 Equal(t, "error: oauth state param could not be verified", err.Error()) 133 } 134 135 func Test_Authentication_NoCodeError(t *testing.T) { 136 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 137 request.Header.Set("Cookie", "oauthState=theState") 138 request.URL, _ = url.Parse("http://localhost/callback?state=theState") 139 140 _, err := Authenticate(testConfig, request) 141 142 Error(t, err) 143 Equal(t, "error: no auth code provided", err.Error()) 144 } 145 146 func Test_Authentication_Provider500(t *testing.T) { 147 // mock a server for token exchange 148 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 149 w.WriteHeader(500) 150 })) 151 defer server.Close() 152 153 testConfigCopy := testConfig 154 testConfigCopy.TokenURL = server.URL 155 156 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 157 request.Header.Set("Cookie", "oauthState=theState") 158 request.URL, _ = url.Parse("http://localhost/callback?code=theCode&state=theState") 159 160 _, err := Authenticate(testConfigCopy, request) 161 162 Error(t, err) 163 Equal(t, "error: expected http status 200 on token exchange, but got 500", err.Error()) 164 } 165 166 func Test_Authentication_ProviderNetworkError(t *testing.T) { 167 168 testConfigCopy := testConfig 169 testConfigCopy.TokenURL = "http://localhost:12345678" 170 171 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 172 request.Header.Set("Cookie", "oauthState=theState") 173 request.URL, _ = url.Parse("http://localhost/callback?code=theCode&state=theState") 174 175 _, err := Authenticate(testConfigCopy, request) 176 177 Error(t, err) 178 Contains(t, err.Error(), "invalid port") 179 } 180 181 func Test_Authentication_TokenParseError(t *testing.T) { 182 // mock a server for token exchange 183 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 184 w.WriteHeader(200) 185 w.Header().Set("Content-Type", "application/json") 186 w.Write([]byte(`{"access_t`)) 187 188 })) 189 defer server.Close() 190 191 testConfigCopy := testConfig 192 testConfigCopy.TokenURL = server.URL 193 194 request, _ := http.NewRequest("GET", testConfig.RedirectURI, nil) 195 request.Header.Set("Cookie", "oauthState=theState") 196 request.URL, _ = url.Parse("http://localhost/callback?code=theCode&state=theState") 197 198 _, err := Authenticate(testConfigCopy, request) 199 200 Error(t, err) 201 Equal(t, "error on parsing oauth token: unexpected end of JSON input", err.Error()) 202 }