Authentication
Complete guide to user authentication and management with the Cocobase Go SDK.
Table of Contents
- Overview
- Registration
- Login
- Logout
- User Information
- Update User
- Authentication State
- Role-Based Access
- Token Persistence
- Complete Examples
Overview
Cocobase provides a complete authentication system with:
- Email/password authentication
- Automatic token management
- Optional token persistence
- Role-based access control
- User profile management
Registration
Basic Registration
err := client.Register(ctx, "user@example.com", "securePassword123", nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("User registered successfully")
Registration with Additional Data
userData := map[string]interface{}{
"firstName": "Alice",
"lastName": "Smith",
"phone": "+1234567890",
"country": "US",
}
err := client.Register(ctx, "alice@example.com", "password123", userData)
if err != nil {
log.Fatal(err)
}
Handling Registration Errors
err := client.Register(ctx, email, password, data)
if err != nil {
if apiErr, ok := err.(*cocobase.APIError); ok {
switch apiErr.StatusCode {
case 409:
fmt.Println("Email already exists")
case 400:
fmt.Println("Invalid email or password")
default:
fmt.Printf("Registration failed: %s\n", apiErr.Suggestion)
}
} else {
log.Fatal(err)
}
}
Login
Basic Login
err := client.Login(ctx, "user@example.com", "securePassword123")
if err != nil {
log.Fatal(err)
}
fmt.Println("Logged in successfully")
Login with Error Handling
err := client.Login(ctx, email, password)
if err != nil {
if apiErr, ok := err.(*cocobase.APIError); ok {
switch apiErr.StatusCode {
case 401:
fmt.Println("Invalid credentials")
case 403:
fmt.Println("Account is locked or banned")
default:
fmt.Printf("Login failed: %s\n", apiErr.Suggestion)
}
} else {
log.Fatal(err)
}
}
Auto-Login on Registration
The Register method automatically logs the user in after successful registration. You don't need to call Login separately:
// Register automatically logs the user in
err := client.Register(ctx, "user@example.com", "password", nil)
if err != nil {
log.Fatal(err)
}
// You can immediately access the user
user, err := client.GetCurrentUser(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Welcome, %s!\n", user.Email)
Logout
Basic Logout
err := client.Logout()
if err != nil {
log.Fatal(err)
}
fmt.Println("Logged out successfully")
Logout:
- Clears the authentication token
- Clears cached user data
- Removes stored token (if using storage)
Safe Logout
// Logout is safe to call even if not logged in
err := client.Logout()
if err != nil {
log.Printf("Warning: Logout error: %v\n", err)
}
User Information
Get Current User
user, err := client.GetCurrentUser(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User ID: %s\n", user.ID)
fmt.Printf("Email: %s\n", user.Email)
fmt.Printf("Roles: %v\n", user.Roles)
fmt.Printf("Data: %+v\n", user.Data)
User Struct
type AppUser struct {
ID string `json:"id"`
Email string `json:"email"`
Roles []string `json:"roles"`
Data map[string]interface{} `json:"data"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Accessing User Data
user, err := client.GetCurrentUser(ctx)
if err != nil {
log.Fatal(err)
}
// Access built-in fields
fmt.Printf("Email: %s\n", user.Email)
// Access custom data
if firstName, ok := user.Data["firstName"].(string); ok {
fmt.Printf("First Name: %s\n", firstName)
}
if age, ok := user.Data["age"].(float64); ok {
fmt.Printf("Age: %.0f\n", age)
}
Update User
Update User Data
newData := map[string]interface{}{
"phone": "+1234567890",
"country": "US",
}
user, err := client.UpdateUser(ctx, newData, nil, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Updated user: %+v\n", user.Data)
Update Email
newEmail := "newemail@example.com"
user, err := client.UpdateUser(ctx, nil, &newEmail, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("New email: %s\n", user.Email)
Update Password
newPassword := "newSecurePassword456"
user, err := client.UpdateUser(ctx, nil, nil, &newPassword)
if err != nil {
log.Fatal(err)
}
fmt.Println("Password updated successfully")
Update Multiple Fields
newData := map[string]interface{}{
"phone": "+1234567890",
"bio": "Software developer",
}
newEmail := "newemail@example.com"
newPassword := "newPassword123"
user, err := client.UpdateUser(ctx, newData, &newEmail, &newPassword)
if err != nil {
log.Fatal(err)
}
fmt.Println("All fields updated successfully")
Data Merging
When updating user data, new fields are merged with existing ones:
// Initial data: {"firstName": "Alice", "age": 28}
newData := map[string]interface{}{
"age": 29, // Updates existing field
"city": "NYC", // Adds new field
}
user, err := client.UpdateUser(ctx, newData, nil, nil)
// Result: {"firstName": "Alice", "age": 29, "city": "NYC"}
Authentication State
Check if Authenticated
if client.IsAuthenticated() {
fmt.Println("User is logged in")
} else {
fmt.Println("User is not logged in")
}
Get Current Token
token := client.GetToken()
if token != "" {
fmt.Printf("Token: %s\n", token)
}
Manual Token Management
// Set a token manually (not recommended for normal use)
err := client.SetToken("your-jwt-token")
if err != nil {
log.Fatal(err)
}
Role-Based Access
Check User Roles
if client.HasRole("admin") {
fmt.Println("User is an admin")
}
if client.HasRole("moderator") {
fmt.Println("User is a moderator")
}
Role-Based Logic
user, err := client.GetCurrentUser(ctx)
if err != nil {
log.Fatal(err)
}
// Check multiple roles
isStaff := false
for _, role := range user.Roles {
if role == "admin" || role == "moderator" || role == "support" {
isStaff = true
break
}
}
if isStaff {
fmt.Println("User is a staff member")
}
Protected Operations
func deleteUser(client *cocobase.Client, ctx context.Context, userID string) error {
// Check if user has permission
if !client.HasRole("admin") {
return fmt.Errorf("permission denied: admin role required")
}
return client.DeleteDocument(ctx, "users", userID)
}
Token Persistence
Using Memory Storage
import "github.com/lordace-coder/cocobase-go/storage"
// Tokens persist in memory (until app restarts)
store := storage.NewMemoryStorage()
client := cocobase.NewClient(cocobase.Config{
APIKey: "your-api-key",
Storage: store,
})
Using File Storage
import "github.com/lordace-coder/cocobase-go/storage"
// Tokens persist to file
store, err := storage.NewFileStorage(".cocobase/auth.json")
if err != nil {
log.Fatal(err)
}
client := cocobase.NewClient(cocobase.Config{
APIKey: "your-api-key",
Storage: store,
})
Initialize Auth from Storage
// Create client with storage
store, _ := storage.NewFileStorage(".cocobase/auth.json")
client := cocobase.NewClient(cocobase.Config{
APIKey: "your-api-key",
Storage: store,
})
// Load existing auth state
err := client.InitAuth(ctx)
if err != nil {
log.Println("No existing auth state")
} else {
fmt.Println("Restored authentication")
}
Complete Persistence Flow
package main
import (
"context"
"fmt"
"log"
"github.com/lordace-coder/cocobase-go/cocobase"
"github.com/lordace-coder/cocobase-go/storage"
)
func main() {
ctx := context.Background()
// Setup client with file storage
store, err := storage.NewFileStorage(".cocobase/auth.json")
if err != nil {
log.Fatal(err)
}
client := cocobase.NewClient(cocobase.Config{
APIKey: "your-api-key",
Storage: store,
})
// Try to restore existing session
err = client.InitAuth(ctx)
if err != nil {
// No existing session, need to login
err = client.Login(ctx, "user@example.com", "password")
if err != nil {
log.Fatal(err)
}
fmt.Println("Logged in")
} else {
fmt.Println("Restored previous session")
}
// Now authenticated, can make requests
user, err := client.GetCurrentUser(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Welcome back, %s!\n", user.Email)
}
Complete Examples
Registration Flow
func registerUser(client *cocobase.Client) error {
ctx := context.Background()
// Get user input (in real app, from form)
email := "alice@example.com"
password := "securePassword123"
userData := map[string]interface{}{
"firstName": "Alice",
"lastName": "Smith",
"country": "US",
}
// Register
err := client.Register(ctx, email, password, userData)
if err != nil {
return fmt.Errorf("registration failed: %w", err)
}
// Get user info (automatically logged in)
user, err := client.GetCurrentUser(ctx)
if err != nil {
return fmt.Errorf("failed to get user: %w", err)
}
fmt.Printf("Welcome, %s!\n", user.Email)
return nil
}
Login Flow
func loginUser(client *cocobase.Client) error {
ctx := context.Background()
// Get credentials
email := "user@example.com"
password := "password123"
// Attempt login
err := client.Login(ctx, email, password)
if err != nil {
if apiErr, ok := err.(*cocobase.APIError); ok {
switch apiErr.StatusCode {
case 401:
return fmt.Errorf("invalid email or password")
case 403:
return fmt.Errorf("account is locked")
default:
return fmt.Errorf("login failed: %s", apiErr.Suggestion)
}
}
return err
}
// Get user info
user, err := client.GetCurrentUser(ctx)
if err != nil {
return fmt.Errorf("failed to get user: %w", err)
}
fmt.Printf("Welcome back, %s!\n", user.Email)
return nil
}
Profile Update Flow
func updateProfile(client *cocobase.Client) error {
ctx := context.Background()
// Check if authenticated
if !client.IsAuthenticated() {
return fmt.Errorf("not authenticated")
}
// Update user data
newData := map[string]interface{}{
"phone": "+1234567890",
"bio": "Software developer",
"website": "https://example.com",
}
user, err := client.UpdateUser(ctx, newData, nil, nil)
if err != nil {
return fmt.Errorf("update failed: %w", err)
}
fmt.Printf("Profile updated: %+v\n", user.Data)
return nil
}
Protected Resource Access
func accessProtectedResource(client *cocobase.Client) error {
ctx := context.Background()
// Check authentication
if !client.IsAuthenticated() {
return fmt.Errorf("authentication required")
}
// Check role
if !client.HasRole("admin") {
return fmt.Errorf("admin access required")
}
// Access protected resource
docs, err := client.ListDocuments(ctx, "admin_logs", nil)
if err != nil {
return err
}
fmt.Printf("Found %d admin logs\n", len(docs))
return nil
}
Session Management
func manageSession(client *cocobase.Client) {
ctx := context.Background()
// Try to restore session
err := client.InitAuth(ctx)
if err == nil {
fmt.Println("Session restored")
return
}
// No session, show login
fmt.Println("Please log in")
// After login...
err = client.Login(ctx, "user@example.com", "password")
if err != nil {
log.Fatal(err)
}
// Session is now active
fmt.Println("Logged in successfully")
}
Best Practices
1. Always Use Context
// Good: Use context for cancellation/timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := client.Login(ctx, email, password)
2. Use Storage for Persistence
// Good: Enable token persistence
store, _ := storage.NewFileStorage(".cocobase/auth.json")
client := cocobase.NewClient(cocobase.Config{
APIKey: "your-api-key",
Storage: store,
})
3. Check Authentication Before Protected Operations
// Good: Check auth state
if !client.IsAuthenticated() {
return fmt.Errorf("authentication required")
}
// Proceed with operation...
4. Handle Errors Gracefully
// Good: Provide user-friendly error messages
err := client.Login(ctx, email, password)
if err != nil {
if apiErr, ok := err.(*cocobase.APIError); ok {
switch apiErr.StatusCode {
case 401:
showError("Invalid email or password")
case 429:
showError("Too many login attempts. Please try again later.")
}
}
}
5. Logout on Exit
// Good: Clean up on exit
defer client.Logout()
Previous: Query Builder | Next: Real-time Updates →