Skip to main content

Integrate authentication into Go

This guide shows how to create a simple Go application and secure it with authentication powered by Ory. You can use this guide with both Ory Network and self-hosted Ory software.

This guide is perfect for you if:

  1. You have Go installed.
  2. You want to build an app using Go.
  3. You want to give access to your application to signed-in users only.

Before you start, watch this video to see the user flow you're going to implement:

info

You can find the code of the sample application here.

Create Go app

First, create a Go app. Run these commands:

mkdir your-project
cd your-project

touch main.go handler.go middleware.go index.html
go mod init github.com/<your-name>/your-project

Install Ory SDK

Run this command to install the Ory SDK which allows you to interact with Ory APIs:

go get -u github.com/ory/client-go

Install Ory CLI

Follow this guide to install the Ory CLI on your machine.

Why do I Need the Ory CLI?

Ory CLI provides a convenient way to configure and manage projects. Additionally, the CLI contains Ory Proxy - a reverse proxy that rewrites cookies to match the domain your application is currently on.

Ory Proxy is a reverse proxy deployed in front of your application. The Proxy mirrors Ory endpoints on the same domain as the application you're running and rewrites cookies to match the domain your application is currently on.

As a result, the origin of the cookies is set correctly to the domain you run the app on instead of

<your-project-slug>.projects.oryapis.com

This behavior is necessary to avoid issues with the browser CORS policy.

By using the Proxy, you can easily connect the application you're developing locally to Ory Network and consume the APIs without additional configuration or the self-hosting any Ory services.

Ory Proxy mirrors Ory's APIs

tip

To learn more about the Ory Proxy, read the dedicated section of the Ory CLI documentation.

Create HTTP server

Use this code to set up a basic server that creates an Ory client, registers a new route for the Go app and uses middleware to validate if the user is allowed to access the application.

Add the code to the main.go file:


package main

import (
"fmt"
"net/http"
"os"

ory "github.com/ory/client-go"
)

type App struct {
ory *ory.APIClient
}

func main() {
proxyPort := os.Getenv("PROXY_PORT")
if proxyPort == "" {
proxyPort = "4000"
}

// register a new Ory client with the URL set to the Ory CLI Proxy
// we can also read the URL from the env or a config file
c := ory.NewConfiguration()
c.Servers = ory.ServerConfigurations{{URL: fmt.Sprintf("http://localhost:%s/.ory", proxyPort)}}

app := &App{
ory: ory.NewAPIClient(c),
}
mux := http.NewServeMux()

// dashboard
mux.Handle("/", app.sessionMiddleware(app.dashboardHandler()))

port := os.Getenv("PORT")
if port == "" {
port = "3000"
}

fmt.Printf("Application launched and running on http://127.0.0.1:%s\n", port)
// start the server
http.ListenAndServe(":"+port, mux)
}

Validate and login

Next, create middleware that checks if the user has a valid session. The session is considered valid when the user is authenticated.

The middleware passes the current request cookies to the Ory client to check if the session is valid.

If the session is valid, the user is presented with the protected page. When the session is not valid, which means that the user is not authenticated or the session expired, the request is redirected for login using the Ory Account Experience.

To create the middleware, add the code to the middleware.go file:


package main

import (
"context"
"log"
"net/http"

ory "github.com/ory/client-go"
)

// save the cookies for any upstream calls to the Ory apis
func withCookies(ctx context.Context, v string) context.Context {
return context.WithValue(ctx, "req.cookies", v)
}

func getCookies(ctx context.Context) string {
return ctx.Value("req.cookies").(string)
}

// save the session to display it on the dashboard
func withSession(ctx context.Context, v *ory.Session) context.Context {
return context.WithValue(ctx, "req.session", v)
}

func getSession(ctx context.Context) *ory.Session {
return ctx.Value("req.session").(*ory.Session)
}

func (app *App) sessionMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
log.Printf("handling middleware request\n")

// set the cookies on the ory client
var cookies string

// this example passes all request.Cookies
// to `ToSession` function
//
// However, you can pass only the value of
// ory_session_projectid cookie to the endpoint
cookies = request.Header.Get("Cookie")

// check if we have a session
session, _, err := app.ory.FrontendApi.ToSession(request.Context()).Cookie(cookies).Execute()
if (err != nil && session == nil) || (err == nil && !*session.Active) {
// this will redirect the user to the managed Ory Login UI
http.Redirect(writer, request, "/.ory/self-service/login/browser", http.StatusSeeOther)
return
}

ctx := withCookies(request.Context(), cookies)
ctx = withSession(ctx, session)

// continue to the requested page (in our case the Dashboard)
next.ServeHTTP(writer, request.WithContext(ctx))
return
}
}

The protected page

As the final step, create a Dashboard page that's presented to signed-in users. This page diplays the user's session data.

  1. Create the dashboardHandler that renders the page with the session data. Add this code to the handler.go file:

package main

import (
"encoding/json"
"html/template"
"net/http"
)

func (app *App) dashboardHandler() http.HandlerFunc {
return func(writer http.ResponseWriter, request *http.Request) {
tmpl, err := template.New("index.html").ParseFiles("index.html")
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
session, err := json.Marshal(getSession(request.Context()))
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
err = tmpl.ExecuteTemplate(writer, "index.html", string(session))
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
}
}
  1. To present the data to the user, create the Dashboard HTML page. Add this code to the index.html file:
<html lang="en">
<head>
<title>Ory Network secured Go web app</title>
</head>
<body>
<h1>Dashboard</h1>
<hr />
<h2>Your Session Data:</h2>
<code>{{ . }}</code>
</body>
</html>

Test your application

With all of the pieces in place, it's time to test your application. Follow these steps:

  1. Start your Go app:
 go run .
  1. Export the SDK configuration URL for the desired Ory project. You can use the provided playground project for testing, or export the SDK URL of your own project.
info

To get your project's SDK URL, sign in at console.ory.sh, select Access & APIs from the left navigation panel, and copy the URL from the SDK Configuration section.

# This is a public Ory Network Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up to Ory Network at
#
# https://console.ory.sh/registration
#
# and create a free Ory Network Project to see your own configuration embedded in code samples.
export ORY_SDK_URL=https://{project.slug}.projects.oryapis.com
  1. Run the Ory proxy to mirror the Ory API endpoints on your application's domain (localhost):
ory proxy http://localhost:3000
  1. Open http://localhost:4000 to access the application. As the initial call is made by an unauthenticated user, the middleware doesn't detect a valid session and redirects to the login page of the defined Ory project.

    From there, you can create a new account or sign in using an existing identity. When you sign in, the session becomes valid and the application shows the `Dashboard` page with the session data.

Go to production

You can use many different approaches to go to production with your application. You can deploy it on Kubernetes, AWS, a VM, a RaspberryPi - the choice is yours! To connect the application to your Ory project, the app and Ory APIs must be available under the same common domain, for example https://ory.example.com and https://www.example.com.

You can easily connect Ory to your subdomain. To do that, add a Custom Domain to your Ory Network project.

With the custom domain set up, you don't need to use Ory Proxy or Ory Tunnel to interact with Ory APIs. Instead, use the configured custom domain in your SDK calls:

// register a new Ory client with the URL set to the Ory CLI Proxy
// we can also read the URL from the env or a config file
c := ory.NewConfiguration()
c.Servers = ory.ServerConfigurations{{URL: "https://ory.example.org"}}