Protect a Page with Login in Go
This guide shows how to create a simple Go application and secure access to the app with Ory. You can use this guide with both Ory Cloud and self-hosted Ory software.
This guide is perfect for you if:
- You have Go installed.
- You want to build an app using Go.
- 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?
The Ory CLI includes useful functionality to manage your Ory Cloud Project. But that is not why we require it in this guide!
Ory's philosophy is to make hard things easy for you. For this reason, Ory has deployed measures against all OWASP Top 10 and implements the OWASP Authentication Cheat Sheet along other mechanisms.
Therefore, Ory manages Anti-CSRF Cookies as well as Ory Session Cookies for you. That however requires that Ory and your application run on the same domain!
If your application runs on http://localhost:3000
then Ory needs to be
available on the hostname localhost
as well (e.g. http://localhost:3001
).
That is why we need the Ory CLI, because it has a proxy included which mirrors
Ory's API endpoints on the domain of your application.
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"
ory "github.com/ory/client-go"
"net/http"
"os"
)
type App struct {
ory *ory.APIClient
// save the cookies for any upstream calls to the Ory apis
cookies string
// save the session to display it on the dashboard
session *ory.Session
}
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 Managed UI.
To create the middleware, add the code to the middleware.go
file:
package main
import (
"log"
"net/http"
)
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
for _, cookie := range request.Cookies() {
cookies += cookie.String() + ";"
}
// check if we have a session
session, _, err := app.ory.V0alpha2Api.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
}
app.cookies = cookies
app.session = session
// continue to the requested page (in our case the Dashboard)
next.ServeHTTP(writer, request)
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.
- Create the
dashboardHandler
that renders the page with the session data. Add this code to thehandler.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(app.session)
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
}
}
}
- To present the data to the user, create the
Dashboard
HTML page. Add this code to theindex.html
file:
<html lang="en">
<head>
<title>Ory Cloud 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:
- Start your Go app:
go run .
- 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 Ory Cloud project.
info
To get your project's SDK URL, sign in at console.ory.sh, select Connect from the left navigation panel, and copy the URL from the SDK Configuration section.
- macOS
- Linux
- Windows CMD
- Windows Powershell
- Self-Hosted Ory Kratos
# This is a public Ory Cloud Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up for Ory Cloud at
#
# https://console.ory.sh/registration
#
# and create a free Ory Cloud Project to see your own configuration embedded in code samples!
export ORY_SDK_URL=https://{your-project-slug-here}.projects.oryapis.com
# This is a public Ory Cloud Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up for Ory Cloud at
#
# https://console.ory.sh/registration
#
# and create a free Ory Cloud Project to see your own configuration embedded in code samples!
export ORY_SDK_URL=https://{your-project-slug-here}.projects.oryapis.com
# This is a public Ory Cloud Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up for Ory Cloud at
#
# https://console.ory.sh/registration
#
# and create a free Ory Cloud Project to see your own configuration embedded in code samples!
set ORY_SDK_URL=https://{your-project-slug-here}.projects.oryapis.com
# This is a public Ory Cloud Project.
# Don’t submit any personally identifiable information in requests made with this project.
# Sign up for Ory Cloud at
#
# https://console.ory.sh/registration
#
# and create a free Ory Cloud Project to see your own configuration embedded in code samples!
$Env:ORY_SDK_URL = "https://{your-project-slug-here}.projects.oryapis.com"
Clone and run Ory Kratos locally
git clone --depth 1 --branch master https://github.com/ory/kratos.git
cd kratos
git checkout master
git pull -ff
docker-compose -f quickstart.yml -f contrib/quickstart/kratos/cloud/quickstart.yml up --build --force-recreate -d
and set the environment variable to the exposed port:
export ORY_SDK_URL=http://localhost:4433
- Run the Ory proxy to mirror the Ory API endpoints on your application's
domain (
localhost
):
ory proxy http://localhost:3000
- 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 theDashboard
page with the session data.
Go to Production
Going to production with your app is possible in many ways. Whether you deploy
it on Kubernetes, AWS, a VM, or a RaspberryPi is up to you. To get your app
working with Ory, your app and Ory must be available under the same common
domain (e.g. https://ory.example.com
and https://www.example.com
).
The easiest way to connect Ory to your domain is to connect Ory to a subdomain of yours. You can do this easily by adding a Custom Domain to your Cloud project!
With the custom domain set up, you do not need the Ory Proxy anymore and will 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"}}