pocketbase-client-golang

Pocketbase

So I didn't see a golang sdk for hooking up oauth for pocketbase, so I hammered out this based on example sdk's.

A big help was the built in documentation in the user collection in pocketbase.

type OAuthProvider string // type alias ftw

const (
	GitHub OAuthProvider = "github"
	Google OAuthProvider = "google"
)

type PocketBaseClient struct {
	BaseURL string // deployed to fly.io
}

type AuthResponse struct {
	Token  string `json:"token"`
	Record Record `json:"record"`
}

type Record struct {
	ID       string `json:"id"`
	Username string `json:"username"`
	Email    string `json:"email"`
}

func NewPocketBaseClient(baseURL string) *PocketBaseClient {
	return &PocketBaseClient{BaseURL: baseURL}
}

type TokenVerifyResponse struct {
	Record Record `json:"record"`
	Token  string `json:"token"`
}

func (c *PocketBaseClient) VerifyToken(token string) (*Record, error) {
	url := fmt.Sprintf("%s/api/collections/users/auth-refresh", c.BaseURL)

	// Create request
	req, err := http.NewRequest("POST", url, nil)
	if err != nil {
		return nil, fmt.Errorf("error creating request: %v", err)
	}

	// Add auth header
	req.Header.Set("Authorization", token)

	// Make the request
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("error making request: %v", err)
	}
	defer resp.Body.Close()

	// Check response status
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("token verification failed with status: %d", resp.StatusCode)
	}

	// Decode response
	var verifyResp TokenVerifyResponse
	if err := json.NewDecoder(resp.Body).Decode(&verifyResp); err != nil {
		return nil, fmt.Errorf("error decoding response: %v", err)
	}

	return &verifyResp.Record, nil
}

func (c *PocketBaseClient) AuthenticateWithOAuth(provider OAuthProvider, code string) (*AuthResponse, error) {
	baseURL := "https://pocketbase_url.com" // use your own!
	url := fmt.Sprintf("%s/api/collections/users/auth-with-oauth2", c.BaseURL) 
	redirectURL := fmt.Sprintf("%s/auth/%s/callback", baseURL, provider)

	payload := map[string]string{
		"provider":    string(provider),
		"code":        code,
		"redirectUrl": redirectURL,
	}

	jsonData, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("error marshaling payload: %v", err)
	}

	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("error making request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		bodyBytes, err := io.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}
		bodyString := string(bodyBytes)
		log.Println("body:", bodyString)
		return nil, fmt.Errorf("authentication failed with status: %d", resp.StatusCode)
	}

	var authResp AuthResponse
	if err := json.NewDecoder(resp.Body).Decode(&authResp); err != nil {
		return nil, fmt.Errorf("error decoding response: %v", err)
	}

	return &authResp, nil
}

---

updated: 27 November 2024.

to the Index
/ html