In this short guide you will learn how to set up an Encore auth handler that makes use of Clerk in order to add an integrated signup and login experience to your web app with an Encore based backend.
For all the code and instructions of how to clone and run this example locally, see the Clerk Example in the Encore examples repo.
Prerequisites
This guide assumes you already have an Encore app. If you don't, why not create one using these tutorials:
- Building an event-driven uptime monitor in Go using Encore
- Building an appointment booking systeming in Go using Encore, with a React frontend
Step 1: Set up the auth handler
In your Encore app, install the following module:
$ go get github.com/clerkinc/clerk-sdk-go/clerk
Create a folder and naming it auth
, this is where our authentication related backend code will live.
It's time to define your auth handler. Create auth/auth.go
and paste the following:
package auth
import (
"context"
"encore.dev/beta/auth"
"encore.dev/beta/errs"
"github.com/clerkinc/clerk-sdk-go/clerk"
)
var secrets struct {
ClientSecretKey string
}
// Service struct definition.
// Learn more: encore.dev/docs/primitives/services-and-apis/service-structs
//
//encore:service
type Service struct {
client clerk.Client
}
// initService is automatically called by Encore when the service starts up.
func initService() (*Service, error) {
client, err := clerk.NewClient(secrets.ClientSecretKey)
if err != nil {
return nil, err
}
return &Service{client: client}, nil
}
type UserData struct {
ID string `json:"id"`
Username *string `json:"username"`
FirstName *string `json:"first_name"`
LastName *string `json:"last_name"`
ProfileImageURL string `json:"profile_image_url"`
PrimaryEmailAddressID *string `json:"primary_email_address_id"`
EmailAddresses []clerk.EmailAddress `json:"email_addresses"`
}
// The `encore:authhandler` annotation tells Encore to run this function for all
// incoming API call that requires authentication.
// Learn more: encore.dev/docs/develop/auth#the-auth-handler
//
//encore:authhandler
func (s *Service) AuthHandler(ctx context.Context, token string) (auth.UID, *UserData, error) {
// verify the session
sessClaims, err := s.client.VerifyToken(token)
if err != nil {
return "", nil, &errs.Error{
Code: errs.Unauthenticated,
Message: "invalid token",
}
}
user, err := s.client.Users().Read(sessClaims.Claims.Subject)
if err != nil {
return "", nil, &errs.Error{
Code: errs.Internal,
Message: err.Error(),
}
}
userData := &UserData{
ID: user.ID,
Username: user.Username,
FirstName: user.FirstName,
LastName: user.LastName,
ProfileImageURL: user.ProfileImageURL,
PrimaryEmailAddressID: user.PrimaryEmailAddressID,
EmailAddresses: user.EmailAddresses,
}
return auth.UID(user.ID), userData, nil
}
Clerk credentials
Create a Clerk account if you haven't already. Then, in the Clerk dashboard, create a new applications.
Next, go to the API Keys page for your app. Copy one of the "Secret keys" (the "Publishable Key" will be used by your frontend).
The Secret key
is sensitive and should not be hardcoded in your code/config. Instead, you should store that as an Encore secret.
From your terminal (inside your Encore app directory), run:
$ encore secret set --prod ClientSecretKey
Now you should do the same for the development secret. The most secure way is to create another secret key (Clerk allows you to have multiple).
Once you have a client secret for development, set it similarly to before:
$ encore secret set --dev ClientSecretKey
Frontend
Clerk offers a React SDK for the frontend which makes it really simple to integrate
a login/signup flow inside your web app as well as getting the token required to communicate with your Encore backend.
You can use the useAuth
hook from @clerk/clerk-react
to get the token and send it to your backend.
import { useAuth } from '@clerk/clerk-react';
export default function ExternalDataPage() {
const { getToken, isLoaded, isSignedIn } = useAuth();
if (!isLoaded) {
// Handle loading state however you like
return <div>Loading...</div>;
}
if (!isSignedIn) {
// Handle signed out state however you like
return <div>Sign in to view this page</div>;
}
const fetchDataFromExternalResource = async () => {
const token = await getToken();
// Use token to send to Encore backend when fetching data
return data;
}
return <div>...</div>;
}
For a fully working backend + frontend example see the Clerk Example in the Encore examples repo.
Wrapping up
- ⭐️ Support the project by starring Encore on GitHub.
- Learn more about building Go apps with Encore using these Tutorials.👈
- Find inspiration on what to build with these Open Source App Templates.👈
- If you have questions or want to share your work, join the developers hangout in Encore's community on Discord.👈
Top comments (2)
thanks for this tutorial, @marcuskohlberg! we just featured it on our latest product update here 🙌
fun fact: Clerk's backends are written in Go and the backend that powers our Dashboard experience is a heavy consumer of our open-source Clerk Go SDK!
Awesome, thanks for sharing!