I am trying to interact with Supabase's storage using the S3 protocol in Golang. I have followed Supabase's example provided in JavaScript and translated it into Go, but I keep encountering a SignatureDoesNotMatch error.
I've created a bucket named uploads. I created a new access keys in my supabase project settings (access key and secret key). I put those in my .env file named AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
.
This is the code that i wrote for interacting with supabase's storage using the s3 protocol.
package providers
import (
"context"
"io"
"log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
type S3ProviderImpl interface {
Upload(fileName string, data io.ReadCloser, bucket string) (string, error)
}
type StorageProvider struct {
s3cl *s3.Client
}
func NewStorageProvider() *StorageProvider {
endpoint := "https://sldogsqddfisyaxbeujt.supabase.co/storage/v1/s3"
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("ap-southeast-1"))
if err != nil {
log.Printf("error: %v", err)
return nil
}
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
o.BaseEndpoint = aws.String(endpoint)
o.UsePathStyle = true
})
return &StorageProvider{
s3cl: client,
}
}
// This is a test function
func (provider *StorageProvider) List() {
res, err := provider.s3cl.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
if err != nil {
log.Println(err)
}
for _, object := range res.Buckets {
log.Printf("key=%s ", aws.ToString(object.Name))
}
}
In the main.go
file i created a new StorageProvider
and called the List
function.
package main
import (
"amelia/internal/providers"
"log"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
"github.com/rs/zerolog"
)
func startup() {
log.Println("Loading .env")
err := godotenv.Load(".env")
if err != nil {
log.Panic(err)
}
}
func main() {
startup()
storage := providers.NewStorageProvider()
storage.List()
}
Since supabase only provide instructions on how to use s3 protocol only in javascript I figured to translate it in go. In their examples they wrote this.
import { S3Client } from '@aws-sdk/client-s3';
const client = new S3Client({
forcePathStyle: true,
region: 'project_region',
endpoint: 'https://project_ref.supabase.co/storage/v1/s3',
credentials: {
accessKeyId: 'your_access_key_id',
accessSecretKey: 'your_secret_access_key',
}
})
I read the AWS SDK v2 for go and did tried to do the same. But i always endup with this error.
024/05/21 18:27:53 operation error S3: ListBuckets, https response error StatusCode: 403, RequestID: , HostID: , api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
So i tried to interact with it using the aws cli. It worked. I've set everything the same, the access and secret key, location and the endpoint.
PS C:\Users\josti> aws s3 ls uploads --endpoint=https://sldogsqddfisyaxbeujt.supabase.co/storage/v1/s3
2024-05-20 16:49:24 8269 abc.jpg
I tried to follow other's suggestion such as adding /
at the end of the bucket name, verifying my keys, added ./
or /
before the file name, but nothing seems to work.
OS: Windows 11 24h2
Go: 1.22
AWS SDK version:
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9
Edit: Included the main.go
file for clarification.
For anyone else who spends way too much time on this like I did; I had success just using the v4 request signer (github.com/aws/aws-sdk-go-v2/aws/signer/v4) and calling s3 myself like below:
req, err := http.NewRequest("GET", "{supabaseurl}/{bucketname}/{key}", nil)
if err != nil {
return "", err
}
creds := aws.Credentials{
AccessKeyID: "supabase-s3-access-key",
SecretAccessKey: "supabase-s3-secret-key",
}
err = signer.SignHTTP(context.Background(),
creds,
req,
// empty string hash, no payload
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"s3",
"region",
time.Now(),
)
if err != nil {
return "", err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()