Skip to content

feat: identities getting started #2103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c492bc6
feat: identities getting started
christiannwamba Apr 8, 2025
7cb4fb6
chore: format
christiannwamba Apr 8, 2025
fa3d902
docs: update identities get-started
christiannwamba Apr 10, 2025
b6a74e9
chore: format
christiannwamba Apr 10, 2025
fa72745
docs: refine based on feedback
christiannwamba Apr 10, 2025
8065cff
docs: fix image cut offs
christiannwamba Apr 10, 2025
0574f12
chore: format
christiannwamba Apr 10, 2025
f9a7a4e
docs: test js example
christiannwamba Apr 11, 2025
e56c868
chore: format
christiannwamba Apr 11, 2025
a56133f
docs: test all walkthroughs and refacot examples
christiannwamba Apr 14, 2025
65629d1
chore: format
christiannwamba Apr 14, 2025
e9740dc
chore: use consistent titles
christiannwamba Apr 14, 2025
d68e779
chore: resolve feedback
christiannwamba Apr 14, 2025
7c7444c
chore: format
christiannwamba Apr 14, 2025
dc74030
docs: pre-requisites and user assumptions
christiannwamba Apr 16, 2025
e524150
chore: remove secure
christiannwamba Apr 17, 2025
4f5b89e
chore: remove bold from texts
christiannwamba Apr 17, 2025
97854b4
chore: highlight technical tokens
christiannwamba Apr 17, 2025
71797da
chore: remove unnecessary word
christiannwamba Apr 17, 2025
f737215
chore: use reusable setup section
christiannwamba Apr 25, 2025
5641a4a
chore: test code snippets and resolve feedback
christiannwamba Apr 26, 2025
fb05bc9
chore: format
christiannwamba Apr 26, 2025
38fa916
chore: apply suggestions from code review
vinckr Apr 29, 2025
6bb1d63
chore: format
vinckr May 2, 2025
7142646
chore: remove webauthn quickstart
vinckr May 2, 2025
b7bd776
chore: format
vinckr May 2, 2025
186839b
chore: format
vinckr May 2, 2025
e9d1b70
chore: comment
vinckr May 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/_common/get-started-setup.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Before starting, ensure you have:

1. An [Ory network account](https://console.ory.sh)
2. Your Ory project id
3. Your development environment set up with your framework of choice
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/configure-generic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/configure-google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/general-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/generic-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/login-with-social.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/mfa-outcome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/mfa-recovery.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/oidc-toggle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/one-time-codes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/otc-outcome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/passkey-prompt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/passkeys-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/privileged-sessions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/provider-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/session-cookie.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/session-lifespan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/webauthn-outcome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/identities/_assets/webauthn-passkeys.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// dashboard_handler.go
package main

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

// dashboardHandler simply displays the session information
func (app *App) dashboardHandler(writer http.ResponseWriter, request *http.Request) {
// Get the session from context
session, err := getSession(request.Context())
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}

// Return the session data as JSON
writer.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(writer)
encoder.SetIndent("", " ")
encoder.Encode(session)
}
8 changes: 8 additions & 0 deletions docs/identities/get-started/_common/code-examples/go/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module go-start

go 1.24.0

require (
github.com/ory/client-go v1.20.6 // indirect
golang.org/x/oauth2 v0.29.0 // indirect
)
4 changes: 4 additions & 0 deletions docs/identities/get-started/_common/code-examples/go/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/ory/client-go v1.20.6 h1:1oUH4y1N8YCgR292FGw+atimSUgafMMIV8sZb36dH5U=
github.com/ory/client-go v1.20.6/go.mod h1:10sCeMADXhQ9GbGhgfSFfj9Pzpnue/ZAVJFSwy/L1FU=
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"io"
"net/http"
)

// LoginHandler handles the /login route
func (app *App) loginHandler(writer http.ResponseWriter, request *http.Request) {
// Get cookies from the request
cookies := request.Header.Get("Cookie")

// Try to verify session with Ory
session, response, err := app.ory.FrontendAPI.ToSession(request.Context()).Cookie(cookies).Execute()

// If there's an error or session is not active, redirect to login UI
if err != nil || (err == nil && !*session.Active) {
http.Redirect(writer, request, app.tunnelUrl+"/self-service/login/browser", http.StatusSeeOther)
return
}

// If session is valid, send the session data as JSON response
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
// Use io.Copy to copy the response body to the writer
io.Copy(writer, response.Body)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"log"
"net/http"
)

// LogoutHandler handles the /logout route
func (app *App) logoutHandler(writer http.ResponseWriter, request *http.Request) {
// Get cookies from the request
cookies := request.Header.Get("Cookie")

// Create a logout flow
logoutFlow, _, err := app.ory.FrontendAPI.CreateBrowserLogoutFlow(request.Context()).
Cookie(cookies).
Execute()

if err != nil {
log.Printf("Error creating logout flow: %v", err)
// Redirect to home page if there's an error
http.Redirect(writer, request, "/", http.StatusSeeOther)
return
}

// Redirect to the logout URL
http.Redirect(writer, request, logoutFlow.LogoutUrl, http.StatusSeeOther)
}
48 changes: 48 additions & 0 deletions docs/identities/get-started/_common/code-examples/go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

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

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

// App holds application state
type App struct {
ory *ory.APIClient
tunnelUrl string
}

func main() {
oryClient, baseUrl := ConfigureOryClient()

// Initialize application
app := &App{
ory: oryClient,
tunnelUrl: baseUrl,
}

// Setup routes
mux := http.NewServeMux()
mux.HandleFunc("/login", app.loginHandler)
mux.HandleFunc("/logout", app.logoutHandler)
mux.HandleFunc("/refresh-session", app.refreshSessionHandler)
mux.Handle("/", app.sessionMiddleware(app.dashboardHandler))

// Get port from environment or use default
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}

// Print tunnel command for convenience
fmt.Printf("Starting server on port %s. Use tunnel with:\n", port)
fmt.Printf("npx @ory/cli tunnel --dev http://localhost:%s\n", port)

// Start the server
err := http.ListenAndServe(":"+port, mux)
if err != nil {
fmt.Printf("Could not start server: %s\n", err)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

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

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

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

// Pass cookies to Ory's ToSession endpoint
cookies := request.Header.Get("Cookie")

// Verify session with Ory
session, _, err := app.ory.FrontendAPI.ToSession(request.Context()).Cookie(cookies).Execute()

// Redirect to login if session doesn't exist or is inactive
if err != nil || (err == nil && !*session.Active) {
log.Printf("No active session, redirecting to login\n")
// Redirect to the login page
http.Redirect(writer, request, app.tunnelUrl+"/self-service/login/browser", http.StatusSeeOther)
return
}
// highlight-start
if *session.AuthenticatorAssuranceLevel != "aal2" {
http.Redirect(writer, request, app.tunnelUrl+"/self-service/login/browser?aal=aal2", http.StatusSeeOther)
return
}
// highlight-end

// Add session to context for the handler
ctx := withSession(request.Context(), session)
next.ServeHTTP(writer, request.WithContext(ctx))
}
}

func withSession(ctx context.Context, v *ory.Session) context.Context {
return context.WithValue(ctx, "req.session", v)
}

func getSession(ctx context.Context) (*ory.Session, error) {
session, ok := ctx.Value("req.session").(*ory.Session)
if !ok || session == nil {
return nil, errors.New("session not found in context")
}
return session, nil
}

// Dashboard page protected by middleware
mux.Handle("/", app.sessionMiddleware(app.dashboardHandler))
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

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

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

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

// Pass cookies to Ory's ToSession endpoint
cookies := request.Header.Get("Cookie")

// Verify session with Ory
session, _, err := app.ory.FrontendAPI.ToSession(request.Context()).Cookie(cookies).Execute()

// Redirect to login if session doesn't exist or is inactive
if err != nil || (err == nil && !*session.Active) {
log.Printf("No active session, redirecting to login\n")
// Redirect to the login page
http.Redirect(writer, request, app.tunnelUrl+"/self-service/login/browser", http.StatusSeeOther)
return
}

// Add session to context for the handler
ctx := withSession(request.Context(), session)
next.ServeHTTP(writer, request.WithContext(ctx))
}
}

func withSession(ctx context.Context, v *ory.Session) context.Context {
return context.WithValue(ctx, "req.session", v)
}

func getSession(ctx context.Context) (*ory.Session, error) {
session, ok := ctx.Value("req.session").(*ory.Session)
if !ok || session == nil {
return nil, errors.New("session not found in context")
}
return session, nil
}

// Dashboard page protected by middleware
mux.Handle("/", app.sessionMiddleware(app.dashboardHandler))
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import (
"os"

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

// ConfigureOryClient sets up the Ory client for local development with tunnel
func ConfigureOryClient() (*ory.APIClient, string) {
baseUrl := os.Getenv("ORY_SDK_URL")

// Configure Ory SDK
configuration := ory.NewConfiguration()
configuration.Servers = ory.ServerConfigurations{{URL: baseUrl}}

// Create and return client
return ory.NewAPIClient(configuration), baseUrl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// refresh_handler.go
package main

import (
"net/http"
)

// RefreshSessionHandler handles the /refresh-session route
func (app *App) refreshSessionHandler(writer http.ResponseWriter, request *http.Request) {
// Redirect to Ory login UI with refresh=true parameter
http.Redirect(writer, request, app.tunnelUrl+"/self-service/login/browser?refresh=true", http.StatusSeeOther)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package main

import (
"io"
"net/http"
)

// SignUpHandler handles the /signup route
func (app *App) signUpHandler(writer http.ResponseWriter, request *http.Request) {
// Get cookies from the request
cookies := request.Header.Get("Cookie")

// Try to verify session with Ory
session, response, err := app.ory.FrontendAPI.ToSession(request.Context()).Cookie(cookies).Execute()

// If there's an error or session is not active, redirect to login UI
if err != nil || (err == nil && !*session.Active) {
http.Redirect(writer, request, app.tunnelUrl+"/self-service/registration/browser", http.StatusSeeOther)
return
}

// If session is valid, send the session data as JSON response
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
// Use io.Copy to copy the response body to the writer
io.Copy(writer, response.Body)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
app.get("/", (req, res) => {
ory
.toSession({ cookie: req.header("cookie") })
.then((data) => res.json(data))
.catch(() =>
res.redirect(`${process.env.ORY_SDK_URL}/self-service/login/browser`),
)
})
13 changes: 13 additions & 0 deletions docs/identities/get-started/_common/code-examples/js/logout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Create logout route
app.get("/logout", async (req, res) => {
try {
// Create a logout flow
const { logout_url } = await ory.createBrowserLogoutFlow({
cookie: req.header("cookie"),
})
// Redirect to logout URL
res.redirect(logout_url)
} catch (err) {
res.redirect("/")
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const requireAuth = async (req, res, next) => {
try {
const session = await ory.toSession({ cookie: req.header("cookie") })
// highlight-start
if (session.authenticator_assurance_level === "aal2") {
req.session = session
next()
} else {
res.redirect(
`${process.env.ORY_SDK_URL}/self-service/login/browser?aal=aal2`,
)
}
// highlight-end
} catch (error) {
res.redirect(`${process.env.ORY_SDK_URL}/self-service/login/browser`)
}
}

app.get("/", requireAuth, (req, res) => {
res.json(req.session.identity.traits) // { email: '[email protected]' }
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const requireAuth = async (req, res, next) => {
try {
const session = await ory.toSession({ cookie: req.header("cookie") })
req.session = session
next()
} catch (error) {
res.redirect(`${process.env.ORY_SDK_URL}/self-service/login/browser`)
}
}

app.get("/", requireAuth, (req, res) => {
res.json(req.session.identity.traits) // { email: '[email protected]' }
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
app.get("/refresh-session", async (req, res) => {
// Redirect to login with refresh=true parameter
res.redirect(`${baseUrl}/ui/login?refresh=true`)
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Configuration, FrontendApi } from "@ory/client-fetch"

const baseUrl = process.env.ORY_SDK_URL || "http://localhost:4000"
const ory = new FrontendApi(
new Configuration({
basePath: baseUrl,
}),
)
10 changes: 10 additions & 0 deletions docs/identities/get-started/_common/code-examples/js/sign-up.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
app.get("/", (req, res) => {
ory
.toSession({ cookie: req.header("cookie") })
.then((data) => res.json(data))
.catch(() =>
res.redirect(
`${process.env.ORY_SDK_URL}/self-service/registration/browser`,
),
)
})
Loading
Loading