diff --git a/README.md b/README.md index d500b24..cd3fc7c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ An isomorphic Go client for Supabase. - Realtime listeners for database changes - [x] Integration with [Postgrest](https://github.com/supabase-community/postgrest-go) - Access your database using a REST API generated from your schema & database functions -- [x] Integration with [Gotrue](https://github.com/supabase-community/gotrue-go) +- [x] Integration with [Auth](https://github.com/supabase-community/auth-go) - User authentication, including OAuth, ***email/password***, and native sign-in - [x] Integration with [Supabase Storage](https://github.com/supabase-community/storage-go) - Store files in S3 with additional managed metadata diff --git a/client.go b/client.go index edb8c8b..e1d742e 100644 --- a/client.go +++ b/client.go @@ -5,26 +5,24 @@ import ( "log" "time" + auth "github.com/supabase-community/auth-go" + "github.com/supabase-community/auth-go/types" "github.com/supabase-community/functions-go" - "github.com/supabase-community/gotrue-go" - "github.com/supabase-community/gotrue-go/types" postgrest "github.com/supabase-community/postgrest-go" storage_go "github.com/supabase-community/storage-go" ) const ( REST_URL = "/rest/v1" - STORAGE_URL = "/storage/v1" + STORAGE_URL = "/storage/v1" AUTH_URL = "/auth/v1" FUNCTIONS_URL = "/functions/v1" ) type Client struct { - // Why is this a private field?? - rest *postgrest.Client - Storage *storage_go.Client - // Auth is an interface. We don't need a pointer to an interface. - Auth gotrue.Client + rest *postgrest.Client + Storage *storage_go.Client + Auth auth.Client Functions *functions.Client options clientOptions } @@ -62,7 +60,6 @@ func NewClient(url, key string, options *ClientOptions) (*Client, error) { client := &Client{} client.options.url = url - // map is pass by reference, so this gets updated by rest of function client.options.headers = headers var schema string @@ -74,9 +71,7 @@ func NewClient(url, key string, options *ClientOptions) (*Client, error) { client.rest = postgrest.NewClient(url+REST_URL, schema, headers) client.Storage = storage_go.NewClient(url+STORAGE_URL, key, headers) - // ugly to make auth client use custom URL - tmp := gotrue.New(url, key) - client.Auth = tmp.WithCustomGoTrueURL(url + AUTH_URL) + client.Auth = auth.New(url+AUTH_URL, key) client.Functions = functions.NewClient(url+FUNCTIONS_URL, key, headers) return client, nil @@ -124,7 +119,6 @@ func (c *Client) EnableTokenAutoRefresh(session types.Session) { time.Sleep(sleepDuration) } - // Refresh the token newSession, err := c.RefreshToken(session.RefreshToken) if err != nil { attempt++ @@ -138,7 +132,6 @@ func (c *Client) EnableTokenAutoRefresh(session types.Session) { continue } - // Update the session, reset the attempt counter, and update the expiresAt time c.UpdateAuthSession(newSession) session = newSession attempt = 0 @@ -162,5 +155,4 @@ func (c *Client) UpdateAuthSession(session types.Session) { c.options.headers["Authorization"] = "Bearer " + session.AccessToken c.Storage = storage_go.NewClient(c.options.url+STORAGE_URL, session.AccessToken, c.options.headers) c.Functions = functions.NewClient(c.options.url+FUNCTIONS_URL, session.AccessToken, c.options.headers) - } diff --git a/client_test.go b/client_test.go index b14b6d7..4fd00ee 100644 --- a/client_test.go +++ b/client_test.go @@ -3,17 +3,23 @@ package supabase_test import ( "fmt" "testing" + "time" - "github.com/supabase-community/supabase-go" + "github.com/supabase-community/auth-go/types" + + supa "github.com/supabase-community/supabase-go" ) const ( - API_URL = "https://your-company.supabase.co" - API_KEY = "your-api-key" + API_URL = "https://your-company.supabase.co" + API_KEY = "your-api-key" + TEST_EMAIL = "test@example.com" + TEST_PASSWORD = "test123456" + TEST_PHONE = "+1234567890" ) func TestFrom(t *testing.T) { - client, err := supabase.NewClient(API_URL, API_KEY, nil) + client, err := supa.NewClient(API_URL, API_KEY, nil) if err != nil { fmt.Println("cannot initalize client", err) } @@ -22,7 +28,7 @@ func TestFrom(t *testing.T) { } func TestRpc(t *testing.T) { - client, err := supabase.NewClient(API_URL, API_KEY, nil) + client, err := supa.NewClient(API_URL, API_KEY, nil) if err != nil { fmt.Println("cannot initalize client", err) } @@ -31,7 +37,7 @@ func TestRpc(t *testing.T) { } func TestStorage(t *testing.T) { - client, err := supabase.NewClient(API_URL, API_KEY, nil) + client, err := supa.NewClient(API_URL, API_KEY, nil) if err != nil { fmt.Println("cannot initalize client", err) } @@ -40,10 +46,127 @@ func TestStorage(t *testing.T) { } func TestFunctions(t *testing.T) { - client, err := supabase.NewClient(API_URL, API_KEY, nil) + client, err := supa.NewClient(API_URL, API_KEY, nil) if err != nil { fmt.Println("cannot initalize client", err) } result, err := client.Functions.Invoke("hello_world", map[string]interface{}{"name": "world"}) fmt.Println(result, err) } + +// use the new auth package to test auth functions +func TestAuth(t *testing.T) { + client, err := supa.NewClient(API_URL, API_KEY, nil) + if err != nil { + t.Fatalf("cannot initialize client: %v", err) + } + + t.Run("SignUp with Email", func(t *testing.T) { + signupReq := types.SignupRequest{ + Email: TEST_EMAIL, + Password: TEST_PASSWORD, + } + + resp, err := client.Auth.Signup(signupReq) + if err != nil { + t.Errorf("SignUp failed: %v", err) + } + if resp.User.Email != TEST_EMAIL { + t.Errorf("Expected email %s, got %s", TEST_EMAIL, resp.User.Email) + } + }) + + t.Run("SignIn with Email", func(t *testing.T) { + tokenReq := types.TokenRequest{ + GrantType: "password", + Email: TEST_EMAIL, + Password: TEST_PASSWORD, + } + + resp, err := client.Auth.Token(tokenReq) + if err != nil { + t.Errorf("SignIn failed: %v", err) + } + if resp.User.Email != TEST_EMAIL { + t.Errorf("Expected email %s, got %s", TEST_EMAIL, resp.User.Email) + } + }) + + t.Run("SignIn with Phone", func(t *testing.T) { + tokenReq := types.TokenRequest{ + GrantType: "password", + Phone: TEST_PHONE, + Password: TEST_PASSWORD, + } + + resp, err := client.Auth.Token(tokenReq) + if err != nil { + t.Errorf("Phone SignIn failed: %v", err) + } + if resp.User.Phone != TEST_PHONE { + t.Errorf("Expected phone %s, got %s", TEST_PHONE, resp.User.Phone) + } + }) + + t.Run("Get User", func(t *testing.T) { + // First sign in to get a token + tokenReq := types.TokenRequest{ + GrantType: "password", + Email: TEST_EMAIL, + Password: TEST_PASSWORD, + } + + authResp, err := client.Auth.Token(tokenReq) + if err != nil { + t.Errorf("SignIn failed: %v", err) + } + + // Use the token to get user info + client.UpdateAuthSession(authResp.Session) + user, err := client.Auth.GetUser() + if err != nil { + t.Errorf("Get user failed: %v", err) + } + if user.Email != TEST_EMAIL { + t.Errorf("Expected email %s, got %s", TEST_EMAIL, user.Email) + } + }) + + t.Run("Refresh Token", func(t *testing.T) { + // First sign in to get a token + tokenReq := types.TokenRequest{ + GrantType: "password", + Email: TEST_EMAIL, + Password: TEST_PASSWORD, + } + + authResp, err := client.Auth.Token(tokenReq) + if err != nil { + t.Errorf("SignIn failed: %v", err) + } + + // Wait a moment before refreshing + time.Sleep(1 * time.Second) + + // Refresh the token + refreshReq := types.TokenRequest{ + GrantType: "refresh_token", + RefreshToken: authResp.RefreshToken, + } + + newResp, err := client.Auth.Token(refreshReq) + if err != nil { + t.Errorf("Token refresh failed: %v", err) + } + if newResp.AccessToken == authResp.AccessToken { + t.Error("Expected new access token to be different") + } + }) + + t.Run("Logout", func(t *testing.T) { + err := client.Auth.Logout() + if err != nil { + t.Errorf("Logout failed: %v", err) + } + }) +} diff --git a/go.mod b/go.mod index 9dfebfc..7ab003c 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ toolchain go1.22.1 require ( github.com/joho/godotenv v1.5.1 + github.com/supabase-community/auth-go v1.3.2 github.com/supabase-community/functions-go v0.0.0-20220927045802-22373e6cb51d - github.com/supabase-community/gotrue-go v1.2.0 github.com/supabase-community/postgrest-go v0.0.11 github.com/supabase-community/storage-go v0.7.0 ) diff --git a/go.sum b/go.sum index ab2c669..e2f84bd 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/roja/postgrest-go v0.0.11 h1:vGgsfL4+Tvkpbukneo4fPy/43gbeWSSlKmum9+pw github.com/roja/postgrest-go v0.0.11/go.mod h1:cw6LfzMyK42AOSBA1bQ/HZ381trIJyuui2GWhraW7Cc= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/supabase-community/gotrue-go v1.2.0 h1:Zm7T5q3qbuwPgC6xyomOBKrSb7X5dvmjDZEmNST7MoE= -github.com/supabase-community/gotrue-go v1.2.0/go.mod h1:86DXBiAUNcbCfgbeOPEh0PQxScLfowUbYgakETSFQOw= +github.com/supabase-community/auth-go v1.3.2 h1:ScKhTXGRS8766J8hEeWURRnrTRDAvKwQs1JPTXBEdcY= +github.com/supabase-community/auth-go v1.3.2/go.mod h1:NR/6b0237xb8oUJt/eOmtnp7UyPpaVMrxOAKJsuRTtw= github.com/supabase-community/storage-go v0.7.0 h1:cJ8HLbbnL54H5rHPtHfiwtpRwcbDfA3in9HL/ucHnqA= github.com/supabase-community/storage-go v0.7.0/go.mod h1:oBKcJf5rcUXy3Uj9eS5wR6mvpwbmvkjOtAA+4tGcdvQ= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= diff --git a/test/remote_client.go b/test/remote_client.go index d7df7c1..344157b 100644 --- a/test/remote_client.go +++ b/test/remote_client.go @@ -1,7 +1,3 @@ -// This is basic example for postgrest-go library usage. -// For now this example is represent wanted syntax and bindings for library. -// After core development this test files will be used for CI tests. - package main import ( @@ -10,7 +6,7 @@ import ( "os" "github.com/joho/godotenv" - "github.com/supabase-community/supabase-go" + supa "github.com/supabase-community/supabase-go" ) func main() { @@ -24,17 +20,15 @@ func main() { email := os.Getenv("TESTUSER") password := os.Getenv("TESTUSERPASSWORD") - client, err := supabase.NewClient(projectURL, anonKey, nil) + client, err := supa.NewClient(projectURL, anonKey, nil) if err != nil { fmt.Println("cannot initalize client", err) } client.SignInWithEmailPassword(email, password) - // rooms, _, err := client.From("rooms").Select("*", "", false).ExecuteString() if err != nil { panic(err) } fmt.Println(rooms) - }