Commit de1cf9c
Changed files (2)
bin
test
bin/idp
@@ -170,6 +170,7 @@ class IdentityProvider
return [200, { 'Content-Type' => "application/json" }, [JSON.pretty_generate({
access_token: JWT.new(sub: SecureRandom.uuid, iat: Time.now.to_i).to_jwt,
token_type: "Bearer",
+ issued_token_type: "urn:ietf:params:oauth:token-type:access_token",
expires_in: 3600,
refresh_token: SecureRandom.hex(32)
})]]
test/e2e_test.go
@@ -1,25 +1,23 @@
package main
import (
+ "bytes"
+ "context"
"net/http"
+ "net/url"
"strings"
"testing"
+ "time"
"github.com/playwright-community/playwright-go"
"github.com/stretchr/testify/assert"
"github.com/xlgmokha/x/pkg/env"
"github.com/xlgmokha/x/pkg/serde"
"github.com/xlgmokha/x/pkg/x"
+ "golang.org/x/oauth2"
)
-type OAuthTokens struct {
- AccessToken string `json:"access_token"`
- TokenType string `json:"token_type"`
- ExpiresIn uint64 `json:"expires_in"`
- RefreshToken string `json:"refresh_token"`
-}
-
-func TestHelloWorld(t *testing.T) {
+func TestAuthx(t *testing.T) {
_ = playwright.Install()
pw := x.Must(playwright.Run())
@@ -29,6 +27,8 @@ func TestHelloWorld(t *testing.T) {
}))
page := x.Must(browser.NewPage())
+ client := &http.Client{Timeout: 2 * time.Second}
+
defer func() {
x.Check(browser.Close())
x.Check(pw.Stop())
@@ -36,19 +36,19 @@ func TestHelloWorld(t *testing.T) {
t.Run("SAML", func(t *testing.T) {
t.Run("IdP", func(t *testing.T) {
- t.Run("provides metadata", func(t *testing.T) {
+ t.Run("metadata.xml", func(t *testing.T) {
response := x.Must(http.Get("http://idp.example.com:8080/saml/metadata.xml"))
assert.Equal(t, http.StatusOK, response.StatusCode)
})
})
- t.Run("Service provider", func(t *testing.T) {
- t.Run("provides metadata", func(t *testing.T) {
+ t.Run("SP", func(t *testing.T) {
+ t.Run("metadata.xml", func(t *testing.T) {
response := x.Must(http.Get("http://ui.example.com:8080/saml/metadata.xml"))
assert.Equal(t, http.StatusOK, response.StatusCode)
})
- t.Run("starts a new session with the IdP", func(t *testing.T) {
+ t.Run("ACS", func(t *testing.T) {
x.Must(page.Goto("http://ui.example.com:8080/saml/new"))
action := x.Must(page.Locator("#idp-form").GetAttribute("action"))
assert.Equal(t, "http://idp.example.com:8080/saml/new", action)
@@ -63,19 +63,82 @@ func TestHelloWorld(t *testing.T) {
})
t.Run("OIDC", func(t *testing.T) {
- t.Run("Performs an OIDC login", func(t *testing.T) {
+ t.Run("login", func(t *testing.T) {
x.Must(page.Goto("http://ui.example.com:8080/oidc/new"))
assert.Contains(t, page.URL(), "http://idp.example.com:8080/oauth/authorize")
assert.NoError(t, page.Locator("#submit-button").Click())
assert.Contains(t, page.URL(), "http://ui.example.com:8080/oauth/callback")
content := x.Must(page.Locator("pre").First().InnerText())
- item := x.Must(serde.FromJSON[OAuthTokens](strings.NewReader(content)))
+ item := x.Must(serde.FromJSON[oauth2.Token](strings.NewReader(content)))
assert.NotEmpty(t, item.AccessToken)
assert.Equal(t, "Bearer", item.TokenType)
assert.NotEmpty(t, item.RefreshToken)
- // header = { 'Authorization' => "Bearer #{token}" }
- // http.Get("http://api.example.com:8080/projects.json")
+
+ response := x.Must(http.Get("http://api.example.com:8080/projects.json"))
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ projects := x.Must(serde.FromJSON[[]map[string]string](response.Body))
+ assert.NotNil(t, projects)
+
+ io := bytes.NewBuffer(nil)
+ assert.NoError(t, serde.ToJSON(io, map[string]string{"name": "example"}))
+ request := x.Must(http.NewRequestWithContext(t.Context(), "POST", "http://api.example.com:8080/projects", io))
+ request.Header.Add("Authorization", "Bearer "+item.AccessToken)
+ response = x.Must(client.Do(request))
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ project := x.Must(serde.FromJSON[map[string]string](response.Body))
+ assert.Equal(t, "example", project["name"])
+ })
+ })
+
+ t.Run("OAuth", func(t *testing.T) {
+ conf := &oauth2.Config{
+ ClientID: "client_id",
+ ClientSecret: "client_secret",
+ Scopes: []string{"openid"},
+ Endpoint: oauth2.Endpoint{
+ TokenURL: "http://idp.example.com:8080/oauth/token",
+ AuthURL: "http://idp.example.com:8080/oauth/authorize",
+ },
+ }
+
+ t.Run("authorization code grant", func(t *testing.T) {
+ authURL := conf.AuthCodeURL(
+ "state",
+ oauth2.SetAuthURLParam("client_id", "client_id"),
+ oauth2.SetAuthURLParam("scope", "openid"),
+ oauth2.SetAuthURLParam("redirect_uri", "http://example.org/oauth/callback"),
+ oauth2.SetAuthURLParam("response_type", "code"),
+ oauth2.SetAuthURLParam("response_mode", "fragment"),
+ )
+ x.Must(page.Goto(authURL))
+ assert.NoError(t, page.Locator("#submit-button").Click())
+
+ uri := x.Must(url.Parse(page.URL()))
+ values := x.Must(url.ParseQuery(uri.Fragment))
+ code := values.Get("code")
+
+ ctx := t.Context()
+ ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
+ credentials := x.Must(conf.Exchange(ctx, code))
+ assert.NotEmpty(t, credentials.AccessToken)
+ assert.Equal(t, "Bearer", credentials.TokenType)
+ assert.NotEmpty(t, credentials.RefreshToken)
+
+ t.Run("token is usable against REST API", func(t *testing.T) {
+ client := conf.Client(ctx, credentials)
+ response := x.Must(client.Get("http://api.example.com:8080/projects.json"))
+ assert.Equal(t, http.StatusOK, response.StatusCode)
+ projects := x.Must(serde.FromJSON[[]map[string]string](response.Body))
+ assert.NotNil(t, projects)
+
+ io := bytes.NewBuffer(nil)
+ assert.NoError(t, serde.ToJSON(io, map[string]string{"name": "foo"}))
+ response = x.Must(client.Post("http://api.example.com:8080/projects", "application/json", io))
+ assert.Equal(t, http.StatusCreated, response.StatusCode)
+ project := x.Must(serde.FromJSON[map[string]string](response.Body))
+ assert.Equal(t, "foo", project["name"])
+ })
})
})
}