func NewOAuthPasswordAuthenticator(provider Provider, mapper authapi.UserIdentityMapper) (authenticator.Password, error) { clientConfig, err := provider.NewConfig() if err != nil { return nil, err } // unused for password grants clientConfig.RedirectUrl = "/" client, err := osincli.NewClient(clientConfig) if err != nil { return nil, err } transport, err := provider.GetTransport() if err != nil { return nil, err } client.Transport = transport return &Handler{ provider: provider, clientConfig: clientConfig, client: client, mapper: mapper, }, nil }
func NewExternalOAuthRedirector(provider Provider, state State, redirectURL string, success handlers.AuthenticationSuccessHandler, errorHandler handlers.AuthenticationErrorHandler, mapper authapi.UserIdentityMapper) (*Handler, error) { clientConfig, err := provider.NewConfig() if err != nil { return nil, err } clientConfig.RedirectUrl = redirectURL client, err := osincli.NewClient(clientConfig) if err != nil { return nil, err } transport, err := provider.GetTransport() if err != nil { return nil, err } client.Transport = transport return &Handler{ provider: provider, state: state, clientConfig: clientConfig, client: client, success: success, errorHandler: errorHandler, mapper: mapper, }, nil }
func main() { config := &osincli.ClientConfig{ ClientId: "xxxxxxxxxxxx.apps.googleusercontent.com", ClientSecret: "secret", AuthorizeUrl: "https://accounts.google.com/o/oauth2/auth", TokenUrl: "https://accounts.google.com/o/oauth2/token", RedirectUrl: "http://localhost:14001/appauth", ErrorsInStatusCode: true, SendClientSecretInParams: true, Scope: "https://www.googleapis.com/auth/plus.login", } client, err := osincli.NewClient(config) if err != nil { panic(err) } // create a new request to generate the url areq := client.NewAuthorizeRequest(osincli.CODE) areq.CustomParameters["access_type"] = "online" areq.CustomParameters["approval_prompt"] = "auto" // Home http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { u := areq.GetAuthorizeUrl() w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a>", u.String()))) }) // Auth endpoint http.HandleFunc("/appauth", func(w http.ResponseWriter, r *http.Request) { // parse a token request if areqdata, err := areq.HandleRequest(r); err == nil { treq := client.NewAccessRequest(osincli.AUTHORIZATION_CODE, areqdata) // show access request url (for debugging only) u2 := treq.GetTokenUrl() w.Write([]byte(fmt.Sprintf("Access token URL: %s\n\n", u2.String()))) // exchange the authorize token for the access token ad, err := treq.GetToken() if err == nil { w.Write([]byte(fmt.Sprintf("Access token: %+v\n\n", ad))) AccessPlusApi(ad, w) } else { w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) } } else { w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) } }) http.ListenAndServe(":14001", nil) }
func main() { config := &osincli.ClientConfig{ ClientId: "1234", ClientSecret: "aabbccdd", AuthorizeUrl: "http://localhost:14000/authorize", TokenUrl: "http://localhost:14000/token", RedirectUrl: "http://localhost:14001/appauth", } client, err := osincli.NewClient(config) if err != nil { panic(err) } // create a new request to generate the url areq := client.NewAuthorizeRequest(osincli.CODE) // Home http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { u := areq.GetAuthorizeUrl() w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a>", u.String()))) }) // Auth endpoint http.HandleFunc("/appauth", func(w http.ResponseWriter, r *http.Request) { // parse a token request if areqdata, err := areq.HandleRequest(r); err == nil { treq := client.NewAccessRequest(osincli.AUTHORIZATION_CODE, areqdata) // show access request url (for debugging only) u2 := treq.GetTokenUrl() w.Write([]byte(fmt.Sprintf("Access token URL: %s\n", u2.String()))) // exchange the authorize token for the access token ad, err := treq.GetToken() if err == nil { w.Write([]byte(fmt.Sprintf("Access token: %+v\n", ad))) } else { w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) } } else { w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) } }) http.ListenAndServe(":14001", nil) }
func TestRegistryAndServer(t *testing.T) { ch := make(chan *http.Request, 1) assertServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { ch <- req })) validClient := &oapi.OAuthClient{ ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{assertServer.URL + "/assert"}, } validClientAuth := &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", } testCases := map[string]struct { Client *oapi.OAuthClient ClientAuth *oapi.OAuthClientAuthorization AuthSuccess bool AuthUser user.Info Scope string Check func(*testHandlers, *http.Request) }{ "needs auth": { Client: validClient, Check: func(h *testHandlers, _ *http.Request) { if !h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("expected request to need authentication: %#v", h) } }, }, "needs grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, Check: func(h *testHandlers, _ *http.Request) { if h.AuthNeed || !h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("expected request to need to grant access: %#v", h) } }, }, "has non covered grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"test"}, }, Scope: "test other", Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || !h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("expected request to need to grant access because of uncovered scopes: %#v", h) } }, }, "has covered grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"test", "other"}, }, Scope: "test other", Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("unexpected flow: %#v", h) } }, }, "has auth and grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: validClientAuth, Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil { t.Errorf("unexpected flow: %#v", h) return } if req == nil { t.Errorf("unexpected nil assertion request") return } if code := req.URL.Query().Get("code"); code == "" { t.Errorf("expected query param 'code', got: %#v", req) } }, }, } for _, testCase := range testCases { h := &testHandlers{} h.Authenticate = testCase.AuthSuccess h.User = testCase.AuthUser access, authorize := &test.AccessTokenRegistry{}, &test.AuthorizeTokenRegistry{} client := &test.ClientRegistry{ Client: testCase.Client, } if testCase.Client == nil { client.Err = apierrs.NewNotFound("client", "unknown") } grant := &test.ClientAuthorizationRegistry{ ClientAuthorization: testCase.ClientAuth, } if testCase.ClientAuth == nil { grant.Err = apierrs.NewNotFound("clientAuthorization", "test:test") } storage := registrystorage.New(access, authorize, client, NewUserConversion()) config := osinserver.NewDefaultServerConfig() server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( h, h, h, ), handlers.NewGrantCheck( NewClientAuthorizationGrantChecker(grant), h, h, ), }, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, h, ) mux := http.NewServeMux() server.Install(mux, "") s := httptest.NewServer(mux) oaclientConfig := &osincli.ClientConfig{ ClientId: "test", ClientSecret: "secret", RedirectUrl: assertServer.URL + "/assert", AuthorizeUrl: s.URL + "/authorize", TokenUrl: s.URL + "/token", Scope: testCase.Scope, } oaclient, err := osincli.NewClient(oaclientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } aReq := oaclient.NewAuthorizeRequest(osincli.CODE) if _, err := http.Get(aReq.GetAuthorizeUrl().String()); err != nil { t.Fatalf("unexpected error: %v", err) } var req *http.Request select { case out := <-ch: req = out default: } testCase.Check(h, req) } }
func TestAuthorizeStartFlow(t *testing.T) { storage := teststorage.New() oauthServer := New( NewDefaultServerConfig(), storage, AuthorizeHandlerFunc(func(ar *osin.AuthorizeRequest, w http.ResponseWriter) (bool, error) { ar.Authorized = true return false, nil }), AccessHandlerFunc(func(ar *osin.AccessRequest, w http.ResponseWriter) error { ar.Authorized = true ar.GenerateRefresh = false return nil }), NewDefaultErrorHandler(), ) mux := http.NewServeMux() oauthServer.Install(mux, "") server := httptest.NewServer(mux) ch := make(chan *osincli.AccessData, 1) var oaclient *osincli.Client var authReq *osincli.AuthorizeRequest assertServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data, err := authReq.HandleRequest(r) if err != nil { t.Fatalf("unexpected error: %v", err) } tokenReq := oaclient.NewAccessRequest(osincli.AUTHORIZATION_CODE, data) token, err := tokenReq.GetToken() if err != nil { t.Fatalf("unexpected error: %v", err) } ch <- token })) storage.Clients["test"] = &osin.DefaultClient{ Id: "test", Secret: "secret", RedirectUri: assertServer.URL + "/assert", } oaclientConfig := &osincli.ClientConfig{ ClientId: "test", ClientSecret: "secret", RedirectUrl: assertServer.URL + "/assert", AuthorizeUrl: server.URL + "/authorize", TokenUrl: server.URL + "/token", } osinclient, err := osincli.NewClient(oaclientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } oaclient = osinclient authReq = oaclient.NewAuthorizeRequest(osincli.CODE) config := &oauth2.Config{ ClientID: "test", ClientSecret: "", Scopes: []string{"a_scope"}, Endpoint: oauth2.Endpoint{ AuthURL: server.URL + "/authorize", TokenURL: server.URL + "/token", }, RedirectURL: assertServer.URL + "/assert", } url := config.AuthCodeURL("") client := http.Client{ /*CheckRedirect: func(req *http.Request, via []*http.Request) error { t.Logf("redirect (%d): to %s, %#v", len(via), req.URL, req) return nil }*/} if _, err := client.Get(url); err != nil { t.Fatalf("unexpected error: %v", err) } token := <-ch if token.AccessToken == "" { t.Errorf("unexpected empty access token: %#v", token) } }
// InstallAPI registers endpoints for an OAuth2 server into the provided mux, // then returns an array of strings indicating what endpoints were started // (these are format strings that will expect to be sent a single string value). func (c *AuthConfig) InstallAPI(container *restful.Container) ([]string, error) { mux := c.getMux(container) clientStorage, err := clientetcd.NewREST(c.RESTOptionsGetter) if err != nil { return nil, err } clientRegistry := clientregistry.NewRegistry(clientStorage) combinedOAuthClientGetter := saoauth.NewServiceAccountOAuthClientGetter(c.KubeClient, c.KubeClient, clientRegistry) accessTokenStorage, err := accesstokenetcd.NewREST(c.RESTOptionsGetter, combinedOAuthClientGetter, c.EtcdBackends...) if err != nil { return nil, err } accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage, err := authorizetokenetcd.NewREST(c.RESTOptionsGetter, combinedOAuthClientGetter, c.EtcdBackends...) if err != nil { return nil, err } authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientAuthStorage, err := clientauthetcd.NewREST(c.RESTOptionsGetter, combinedOAuthClientGetter) if err != nil { return nil, err } clientAuthRegistry := clientauthregistry.NewRegistry(clientAuthStorage) errorPageHandler, err := c.getErrorHandler() if err != nil { glog.Fatal(err) } authRequestHandler, authHandler, authFinalizer, err := c.getAuthorizeAuthenticationHandlers(mux, errorPageHandler) if err != nil { glog.Fatal(err) } storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, combinedOAuthClientGetter, registry.NewUserConversion()) config := osinserver.NewDefaultServerConfig() if c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds > 0 { config.AuthorizationExpiration = c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds } if c.Options.TokenConfig.AccessTokenMaxAgeSeconds > 0 { config.AccessExpiration = c.Options.TokenConfig.AccessTokenMaxAgeSeconds } grantChecker := registry.NewClientAuthorizationGrantChecker(clientAuthRegistry) grantHandler := c.getGrantHandler(mux, authRequestHandler, combinedOAuthClientGetter, clientAuthRegistry) server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( authRequestHandler, authHandler, errorPageHandler, ), handlers.NewGrantCheck( grantChecker, grantHandler, errorPageHandler, ), authFinalizer, }, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, osinserver.NewDefaultErrorHandler(), ) server.Install(mux, OpenShiftOAuthAPIPrefix) if err := CreateOrUpdateDefaultOAuthClients(c.Options.MasterPublicURL, c.AssetPublicAddresses, clientRegistry); err != nil { glog.Fatal(err) } browserClient, err := clientRegistry.GetClient(kapi.NewContext(), OpenShiftBrowserClientID) if err != nil { glog.Fatal(err) } osOAuthClientConfig := c.NewOpenShiftOAuthClientConfig(browserClient) osOAuthClientConfig.RedirectUrl = c.Options.MasterPublicURL + path.Join(OpenShiftOAuthAPIPrefix, tokenrequest.DisplayTokenEndpoint) osOAuthClient, _ := osincli.NewClient(osOAuthClientConfig) if len(*c.Options.MasterCA) > 0 { rootCAs, err := cmdutil.CertPoolFromFile(*c.Options.MasterCA) if err != nil { glog.Fatal(err) } osOAuthClient.Transport = knet.SetTransportDefaults(&http.Transport{ TLSClientConfig: &tls.Config{RootCAs: rootCAs}, }) } tokenRequestEndpoints := tokenrequest.NewEndpoints(c.Options.MasterPublicURL, osOAuthClient) tokenRequestEndpoints.Install(mux, OpenShiftOAuthAPIPrefix) // glog.Infof("oauth server configured as: %#v", server) // glog.Infof("auth handler: %#v", authHandler) // glog.Infof("auth request handler: %#v", authRequestHandler) // glog.Infof("grant checker: %#v", grantChecker) // glog.Infof("grant handler: %#v", grantHandler) return []string{ fmt.Sprintf("Started OAuth2 API at %%s%s", OpenShiftOAuthAPIPrefix), }, nil }
func TestOAuthStorage(t *testing.T) { testutil.DeleteAllEtcdKeys() groupMeta := registered.GroupOrDie(api.GroupName) etcdClient, err := testutil.MakeNewEtcdClient() if err != nil { t.Fatalf("unexpected error: %v", err) } etcdHelper := etcdstorage.NewEtcdStorage(etcdClient, kapi.Codecs.LegacyCodec(groupMeta.GroupVersions...), etcdtest.PathPrefix()) accessTokenStorage := accesstokenetcd.NewREST(etcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(etcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(etcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) user := &testUser{UserName: "******", UserUID: "1"} storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, user) oauthServer := osinserver.New( osinserver.NewDefaultServerConfig(), storage, osinserver.AuthorizeHandlerFunc(func(ar *osin.AuthorizeRequest, w http.ResponseWriter) (bool, error) { ar.UserData = "test" ar.Authorized = true return false, nil }), osinserver.AccessHandlerFunc(func(ar *osin.AccessRequest, w http.ResponseWriter) error { ar.UserData = "test" ar.Authorized = true ar.GenerateRefresh = false return nil }), osinserver.NewDefaultErrorHandler(), ) mux := http.NewServeMux() oauthServer.Install(mux, "") server := httptest.NewServer(mux) defer server.Close() ch := make(chan *osincli.AccessData, 1) var oaclient *osincli.Client var authReq *osincli.AuthorizeRequest assertServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { data, err := authReq.HandleRequest(r) if err != nil { t.Fatalf("unexpected error: %v", err) } tokenReq := oaclient.NewAccessRequest(osincli.AUTHORIZATION_CODE, data) token, err := tokenReq.GetToken() if err != nil { t.Fatalf("unexpected error: %v", err) } ch <- token })) clientRegistry.CreateClient(kapi.NewContext(), &api.OAuthClient{ ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{assertServer.URL + "/assert"}, }) storedClient, err := storage.GetClient("test") if err != nil { t.Fatalf("unexpected error: %v", err) } if storedClient.GetSecret() != "secret" { t.Fatalf("unexpected stored client: %#v", storedClient) } oaclientConfig := &osincli.ClientConfig{ ClientId: "test", ClientSecret: "secret", RedirectUrl: assertServer.URL + "/assert", AuthorizeUrl: server.URL + "/authorize", TokenUrl: server.URL + "/token", } osinclient, err := osincli.NewClient(oaclientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } oaclient = osinclient // initialize the assert server client as well authReq = oaclient.NewAuthorizeRequest(osincli.CODE) config := &oauth2.Config{ ClientID: "test", ClientSecret: "", Scopes: []string{"a_scope"}, RedirectURL: assertServer.URL + "/assert", Endpoint: oauth2.Endpoint{ AuthURL: server.URL + "/authorize", TokenURL: server.URL + "/token", }, } url := config.AuthCodeURL("") client := http.Client{ /*CheckRedirect: func(req *http.Request, via []*http.Request) error { t.Logf("redirect (%d): to %s, %#v", len(via), req.URL, req) return nil }*/} resp, err := client.Get(url) if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("unexpected response: %#v", resp) } token := <-ch if token.AccessToken == "" { t.Errorf("unexpected access token: %#v", token) } actualToken, err := accessTokenRegistry.GetAccessToken(kapi.NewContext(), token.AccessToken) if err != nil { t.Fatalf("unexpected error: %v", err) } if actualToken.UserUID != "1" || actualToken.UserName != "test" { t.Errorf("unexpected stored token: %#v", actualToken) } }
// InstallAPI registers endpoints for an OAuth2 server into the provided mux, // then returns an array of strings indicating what endpoints were started // (these are format strings that will expect to be sent a single string value). func (c *AuthConfig) InstallAPI(container *restful.Container) []string { // TODO: register into container mux := container.ServeMux accessTokenStorage := accesstokenetcd.NewREST(c.EtcdHelper) accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage) authorizeTokenStorage := authorizetokenetcd.NewREST(c.EtcdHelper) authorizeTokenRegistry := authorizetokenregistry.NewRegistry(authorizeTokenStorage) clientStorage := clientetcd.NewREST(c.EtcdHelper) clientRegistry := clientregistry.NewRegistry(clientStorage) clientAuthStorage := clientauthetcd.NewREST(c.EtcdHelper) clientAuthRegistry := clientauthregistry.NewRegistry(clientAuthStorage) authRequestHandler, authHandler, authFinalizer, err := c.getAuthorizeAuthenticationHandlers(mux) if err != nil { glog.Fatal(err) } storage := registrystorage.New(accessTokenRegistry, authorizeTokenRegistry, clientRegistry, registry.NewUserConversion()) config := osinserver.NewDefaultServerConfig() if c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds > 0 { config.AuthorizationExpiration = c.Options.TokenConfig.AuthorizeTokenMaxAgeSeconds } if c.Options.TokenConfig.AccessTokenMaxAgeSeconds > 0 { config.AccessExpiration = c.Options.TokenConfig.AccessTokenMaxAgeSeconds } grantChecker := registry.NewClientAuthorizationGrantChecker(clientAuthRegistry) grantHandler := c.getGrantHandler(mux, authRequestHandler, clientRegistry, clientAuthRegistry) server := osinserver.New( config, storage, osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( authRequestHandler, authHandler, handlers.EmptyError{}, ), handlers.NewGrantCheck( grantChecker, grantHandler, handlers.EmptyError{}, ), authFinalizer, }, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, osinserver.NewDefaultErrorHandler(), ) server.Install(mux, OpenShiftOAuthAPIPrefix) CreateOrUpdateDefaultOAuthClients(c.Options.MasterPublicURL, c.AssetPublicAddresses, clientRegistry) osOAuthClientConfig := c.NewOpenShiftOAuthClientConfig(&OSBrowserClientBase) osOAuthClientConfig.RedirectUrl = c.Options.MasterPublicURL + path.Join(OpenShiftOAuthAPIPrefix, tokenrequest.DisplayTokenEndpoint) osOAuthClient, _ := osincli.NewClient(osOAuthClientConfig) if len(*c.Options.MasterCA) > 0 { rootCAs, err := cmdutil.CertPoolFromFile(*c.Options.MasterCA) if err != nil { glog.Fatal(err) } osOAuthClient.Transport = kutil.SetTransportDefaults(&http.Transport{ TLSClientConfig: &tls.Config{RootCAs: rootCAs}, }) } tokenRequestEndpoints := tokenrequest.NewEndpoints(c.Options.MasterPublicURL, osOAuthClient) tokenRequestEndpoints.Install(mux, OpenShiftOAuthAPIPrefix) // glog.Infof("oauth server configured as: %#v", server) // glog.Infof("auth handler: %#v", authHandler) // glog.Infof("auth request handler: %#v", authRequestHandler) // glog.Infof("grant checker: %#v", grantChecker) // glog.Infof("grant handler: %#v", grantHandler) return []string{ fmt.Sprintf("Started OAuth2 API at %%s%s", OpenShiftOAuthAPIPrefix), fmt.Sprintf("Started Login endpoint at %%s%s", OpenShiftLoginPrefix), } }
func runOAuthFlow(t *testing.T, clusterAdminClientConfig *restclient.Config, projectName string, oauthClientConfig *osincli.ClientConfig, authorizationCodes, authorizationErrors chan string, expectGrantSuccess, expectBuildSuccess bool) { oauthRuntimeClient, err := osincli.NewClient(oauthClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } oauthRuntimeClient.Transport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } directHTTPClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }, } // make sure we're prompted for a password authorizeRequest := oauthRuntimeClient.NewAuthorizeRequest(osincli.CODE) authorizeURL := authorizeRequest.GetAuthorizeUrlWithParams("opaque-scope") authorizeHTTPRequest, err := http.NewRequest("GET", authorizeURL.String(), nil) if err != nil { t.Fatalf("unexpected error: %v", err) } authorizeHTTPRequest.Header.Add("X-CSRF-Token", "csrf-01") authorizeResponse, err := directHTTPClient.Do(authorizeHTTPRequest) if err != nil { t.Fatalf("unexpected error: %v", err) } if authorizeResponse.StatusCode != http.StatusUnauthorized { response, _ := httputil.DumpResponse(authorizeResponse, true) t.Fatalf("didn't get an unauthorized:\n %v", string(response)) } // first we should get a redirect to a grant flow authenticatedAuthorizeHTTPRequest1, err := http.NewRequest("GET", authorizeURL.String(), nil) authenticatedAuthorizeHTTPRequest1.Header.Add("X-CSRF-Token", "csrf-01") authenticatedAuthorizeHTTPRequest1.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("harold:any-pass"))) authentictedAuthorizeResponse1, err := directHTTPClient.Transport.RoundTrip(authenticatedAuthorizeHTTPRequest1) if err != nil { t.Fatalf("unexpected error: %v", err) } if authentictedAuthorizeResponse1.StatusCode != http.StatusFound { response, _ := httputil.DumpResponse(authentictedAuthorizeResponse1, true) t.Fatalf("unexpected status :\n %v", string(response)) } // second we get a webpage with a prompt. Yeah, this next bit gets nasty authenticatedAuthorizeHTTPRequest2, err := http.NewRequest("GET", clusterAdminClientConfig.Host+authentictedAuthorizeResponse1.Header.Get("Location"), nil) authenticatedAuthorizeHTTPRequest2.Header.Add("X-CSRF-Token", "csrf-01") authenticatedAuthorizeHTTPRequest2.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("harold:any-pass"))) authentictedAuthorizeResponse2, err := directHTTPClient.Transport.RoundTrip(authenticatedAuthorizeHTTPRequest2) if err != nil { t.Fatalf("unexpected error: %v", err) } if authentictedAuthorizeResponse2.StatusCode != http.StatusOK { response, _ := httputil.DumpResponse(authentictedAuthorizeResponse2, true) t.Fatalf("unexpected status :\n %v", string(response)) } // have to parse the page to get the csrf value. Yeah, that's nasty, I can't think of another way to do it without creating a new grant handler body, err := ioutil.ReadAll(authentictedAuthorizeResponse2.Body) if err != nil { t.Fatalf("unexpected error: %v", err) } if !expectGrantSuccess { if !strings.Contains(string(body), "requested illegal scopes") { t.Fatalf("missing expected message: %v", string(body)) } return } csrfMatches := grantCSRFRegex.FindStringSubmatch(string(body)) if len(csrfMatches) != 2 { response, _ := httputil.DumpResponse(authentictedAuthorizeResponse2, false) t.Fatalf("unexpected body :\n %v\n%v", string(response), string(body)) } thenMatches := grantThenRegex.FindStringSubmatch(string(body)) if len(thenMatches) != 2 { response, _ := httputil.DumpResponse(authentictedAuthorizeResponse2, false) t.Fatalf("unexpected body :\n %v\n%v", string(response), string(body)) } t.Logf("CSRF is %v", csrfMatches) t.Logf("then is %v", thenMatches) // third we respond and approve the grant, then let the transport follow redirects and give us the code postBody := strings.NewReader(url.Values(map[string][]string{ "then": {thenMatches[1]}, "csrf": {csrfMatches[1]}, "client_id": {oauthClientConfig.ClientId}, "user_name": {"harold"}, "scopes": {oauthClientConfig.Scope}, "redirect_uri": {clusterAdminClientConfig.Host}, "approve": {"true"}, }).Encode()) authenticatedAuthorizeHTTPRequest3, err := http.NewRequest("POST", clusterAdminClientConfig.Host+origin.OpenShiftApprovePrefix, postBody) authenticatedAuthorizeHTTPRequest3.Header.Set("Content-Type", "application/x-www-form-urlencoded") authenticatedAuthorizeHTTPRequest3.Header.Add("X-CSRF-Token", csrfMatches[1]) authenticatedAuthorizeHTTPRequest3.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("harold:any-pass"))) for i := range authentictedAuthorizeResponse2.Cookies() { cookie := authentictedAuthorizeResponse2.Cookies()[i] authenticatedAuthorizeHTTPRequest3.AddCookie(cookie) } authentictedAuthorizeResponse3, err := directHTTPClient.Transport.RoundTrip(authenticatedAuthorizeHTTPRequest3) if err != nil { t.Fatalf("unexpected error: %v", err) } if authentictedAuthorizeResponse3.StatusCode != http.StatusFound { response, _ := httputil.DumpResponse(authentictedAuthorizeResponse3, true) t.Fatalf("unexpected status :\n %v", string(response)) } // fourth, the grant redirects us again to have us send the code to the server authenticatedAuthorizeHTTPRequest4, err := http.NewRequest("GET", clusterAdminClientConfig.Host+authentictedAuthorizeResponse3.Header.Get("Location"), nil) authenticatedAuthorizeHTTPRequest4.Header.Add("X-CSRF-Token", "csrf-01") authenticatedAuthorizeHTTPRequest4.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("harold:any-pass"))) authentictedAuthorizeResponse4, err := directHTTPClient.Transport.RoundTrip(authenticatedAuthorizeHTTPRequest4) if err != nil { t.Fatalf("unexpected error: %v", err) } if authentictedAuthorizeResponse4.StatusCode != http.StatusFound { response, _ := httputil.DumpResponse(authentictedAuthorizeResponse4, true) t.Fatalf("unexpected status :\n %v", string(response)) } authenticatedAuthorizeHTTPRequest5, err := http.NewRequest("GET", authentictedAuthorizeResponse4.Header.Get("Location"), nil) authenticatedAuthorizeHTTPRequest5.Header.Add("X-CSRF-Token", "csrf-01") authenticatedAuthorizeHTTPRequest5.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("harold:any-pass"))) authentictedAuthorizeResponse5, err := directHTTPClient.Do(authenticatedAuthorizeHTTPRequest5) if err != nil { t.Fatalf("unexpected error: %v", err) } authorizationCode := "" select { case authorizationCode = <-authorizationCodes: case <-time.After(10 * time.Second): response, _ := httputil.DumpResponse(authentictedAuthorizeResponse5, true) t.Fatalf("didn't get a code:\n %v", string(response)) } accessRequest := oauthRuntimeClient.NewAccessRequest(osincli.AUTHORIZATION_CODE, &osincli.AuthorizeData{Code: authorizationCode}) accessData, err := accessRequest.GetToken() if err != nil { t.Fatalf("unexpected error: %v", err) } whoamiConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig) whoamiConfig.BearerToken = accessData.AccessToken whoamiClient, err := client.New(&whoamiConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := whoamiClient.Builds(projectName).List(kapi.ListOptions{}); !kapierrors.IsForbidden(err) && !expectBuildSuccess { t.Fatalf("unexpected error: %v", err) } user, err := whoamiClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if user.Name != "harold" { t.Fatalf("expected %v, got %v", "harold", user.Name) } }
func main() { // create http muxes serverhttp := http.NewServeMux() clienthttp := http.NewServeMux() // create server config := osin.NewServerConfig() sstorage := example.NewTestStorage() sstorage.SetClient("1234", &osin.DefaultClient{ Id: "1234", Secret: "aabbccdd", RedirectUri: "http://localhost:14001/appauth", }) server := osin.NewServer(config, sstorage) // create client cliconfig := &osincli.ClientConfig{ ClientId: "1234", ClientSecret: "aabbccdd", AuthorizeUrl: "http://localhost:14000/authorize", TokenUrl: "http://localhost:14000/token", RedirectUrl: "http://localhost:14001/appauth", } client, err := osincli.NewClient(cliconfig) if err != nil { panic(err) } // create a new request to generate the url areq := client.NewAuthorizeRequest(osincli.CODE) // SERVER // Authorization code endpoint serverhttp.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) { resp := server.NewResponse() defer resp.Close() if ar := server.HandleAuthorizeRequest(resp, r); ar != nil { if !example.HandleLoginPage(ar, w, r) { return } ar.Authorized = true server.FinishAuthorizeRequest(resp, r, ar) } if resp.IsError && resp.InternalError != nil { fmt.Printf("ERROR: %s\n", resp.InternalError) } osin.OutputJSON(resp, w, r) }) // Access token endpoint serverhttp.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { resp := server.NewResponse() defer resp.Close() if ar := server.HandleAccessRequest(resp, r); ar != nil { ar.Authorized = true server.FinishAccessRequest(resp, r, ar) } if resp.IsError && resp.InternalError != nil { fmt.Printf("ERROR: %s\n", resp.InternalError) } osin.OutputJSON(resp, w, r) }) // Information endpoint serverhttp.HandleFunc("/info", func(w http.ResponseWriter, r *http.Request) { resp := server.NewResponse() defer resp.Close() if ir := server.HandleInfoRequest(resp, r); ir != nil { server.FinishInfoRequest(resp, r, ir) } osin.OutputJSON(resp, w, r) }) // CLIENT // Home clienthttp.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { u := areq.GetAuthorizeUrl() w.Write([]byte(fmt.Sprintf("<a href=\"%s\">Login</a>", u.String()))) }) // Auth endpoint clienthttp.HandleFunc("/appauth", func(w http.ResponseWriter, r *http.Request) { // parse a token request areqdata, err := areq.HandleRequest(r) if err != nil { w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) return } treq := client.NewAccessRequest(osincli.AUTHORIZATION_CODE, areqdata) // show access request url (for debugging only) u2 := treq.GetTokenUrl() w.Write([]byte(fmt.Sprintf("Access token URL: %s\n", u2.String()))) // exchange the authorize token for the access token ad, err := treq.GetToken() if err != nil { w.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) return } w.Write([]byte(fmt.Sprintf("Access token: %+v\n", ad))) }) go http.ListenAndServe(":14001", clienthttp) http.ListenAndServe(":14000", serverhttp) }
func runOAuthFlow( t *testing.T, clusterAdminClientConfig *restclient.Config, projectName string, oauthClientConfig *osincli.ClientConfig, inputFilter htmlutil.InputFilterFunc, authorizationCodes chan string, authorizationErrors chan string, expectGrantSuccess bool, expectBuildSuccess bool, expectOperations []string, ) { drain(authorizationCodes) drain(authorizationErrors) oauthRuntimeClient, err := osincli.NewClient(oauthClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } testTransport := &basicAuthTransport{rt: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}} oauthRuntimeClient.Transport = testTransport authorizeRequest := oauthRuntimeClient.NewAuthorizeRequest(osincli.CODE) req, err := http.NewRequest("GET", authorizeRequest.GetAuthorizeUrlWithParams("opaque-state").String(), nil) if err != nil { t.Fatalf("unexpected error: %v", err) } operations := []string{} jar, _ := cookiejar.New(nil) directHTTPClient := &http.Client{ Transport: testTransport, CheckRedirect: func(redirectReq *http.Request, via []*http.Request) error { glog.Infof("302 Location: %s", redirectReq.URL.String()) req = redirectReq operations = append(operations, "redirect to "+redirectReq.URL.Path) return nil }, Jar: jar, } for { glog.Infof("%s %s", req.Method, req.URL.String()) operations = append(operations, req.Method+" "+req.URL.Path) // Always set the csrf header req.Header.Set("X-CSRF-Token", "1") resp, err := directHTTPClient.Do(req) if err != nil { glog.Infof("%#v", operations) glog.Infof("%#v", jar) glog.Errorf("Error %v\n%#v\n%#v", err, err, resp) t.Errorf("Error %v\n%#v\n%#v", err, err, resp) return } defer resp.Body.Close() // Save the current URL for reference currentURL := req.URL if resp.StatusCode == 401 { // Set up a username and password once we're challenged testTransport.username = "******" testTransport.password = "******" operations = append(operations, "received challenge") continue } if resp.StatusCode != 200 { responseDump, _ := httputil.DumpResponse(resp, true) t.Errorf("Unexpected response %s", string(responseDump)) return } doc, err := html.Parse(resp.Body) if err != nil { t.Error(err) return } forms := htmlutil.GetElementsByTagName(doc, "form") // if there's a single form, submit it if len(forms) > 1 { t.Errorf("More than one form encountered: %d", len(forms)) return } if len(forms) == 0 { break } req, err = htmlutil.NewRequestFromForm(forms[0], currentURL, inputFilter) if err != nil { t.Error(err) return } operations = append(operations, "form") } authorizationCode := "" select { case authorizationCode = <-authorizationCodes: operations = append(operations, "code") case authorizationError := <-authorizationErrors: operations = append(operations, "error:"+authorizationError) case <-time.After(5 * time.Second): t.Error("didn't get a code or an error") } if len(authorizationCode) > 0 { accessRequest := oauthRuntimeClient.NewAccessRequest(osincli.AUTHORIZATION_CODE, &osincli.AuthorizeData{Code: authorizationCode}) accessData, err := accessRequest.GetToken() if err != nil { t.Errorf("unexpected error: %v", err) return } operations = append(operations, fmt.Sprintf("scope:%v", accessData.ResponseData["scope"])) whoamiConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig) whoamiConfig.BearerToken = accessData.AccessToken whoamiClient, err := client.New(&whoamiConfig) if err != nil { t.Errorf("unexpected error: %v", err) return } _, err = whoamiClient.Builds(projectName).List(kapi.ListOptions{}) if expectBuildSuccess && err != nil { t.Errorf("unexpected error: %v", err) return } if !expectBuildSuccess && !kapierrors.IsForbidden(err) { t.Errorf("expected forbidden error, got %v", err) return } user, err := whoamiClient.Users().Get("~") if err != nil { t.Errorf("unexpected error: %v", err) return } if user.Name != "harold" { t.Errorf("expected %v, got %v", "harold", user.Name) return } } if !reflect.DeepEqual(operations, expectOperations) { t.Errorf("Expected:\n%#v\nGot\n%#v", expectOperations, operations) } }