Check session status
Ory stores session data in different ways for browser apps and native apps:
- For browser apps, Ory stores session data in Ory Session Cookies
- For native apps, Ory stores session data in Ory Session Tokens
Refresh your knowledge on the cookie-based security used by Ory and the difference between browser and native APIs.
To check if the user is signed in and has an active session, use the /sessions/whoami endpoint. If the user doesn't have an
active session, the system returns a 401 Unauthorized response. If the user has an active session, you get a 200 OK response
and the session payload.
When using the SDK, use the frontend.toSession() method.
- cURL
- AJAX / SPA
- ExpressJS
- Go
- React Native
To check for an active session with an Ory Session Token, run:
curl -H "Authorization: Bearer {your-session-token}" \
"https://$PROJECT_SLUG.projects.oryapis.com/sessions/whoami"
To check for an active session with an Ory Session Cookie, run:
curl -H "Cookie: ory_session_...=..." \
"https://$PROJECT_SLUG.projects.oryapis.com/sessions/whoami"
When calling Ory or your API from a Single Page Application (SPA) using AJAX:
- The SPA (
www.yourdomain.com,localhost:3000), Ory (ory.yourdomain.com,localhost:4000), and your API must be on the same domain. - You configured CORS in the Ory Console and on your API server.
- The cookie is configured to be available on the domains you need, for example, the full
yourdomain.com.
// Copyright © 2026 Ory Corp
const { FrontendApi, Configuration } = require("@ory/client-fetch")
const ory = new FrontendApi(
new Configuration({
basePath: `{YOUR-CUSTOM-DOMAIN-OR-ORY-TUNNEL}`,
baseOptions: {
withCredentials: true,
},
}),
)
const session = await ory.toSession().catch((err) => {
// Check the error to see if it's a 401 / 403 -> not logged in
})
Ory checks for both the cookie and the token, which means that your API must handle both Ory Session Cookies and Ory Session Tokens:
// Copyright © 2026 Ory Corp
const express = require("express")
const app = express()
const { FrontendApi, Configuration } = require("@ory/client")
const ory = new FrontendApi(
new Configuration({
basePath: `https://${process.env.PROJECT_SLUG}.projects.oryapis.com`,
}),
)
app.get("/blog/posts", async function (req, res) {
const authHeader = req.headers.authorization
const hasAuthHeader = authHeader.startsWith("Bearer ")
const sessionToken = hasAuthHeader
? authHeader.slice(7, authHeader.length)
: null
const session = await ory
.toSession({
cookie: req.cookies.join("; "),
xSessionToken: sessionToken,
})
.catch((err) => {
// Check the error to see if it's a 401 / 403 -> not logged in
})
})
app.listen(3000, function () {
console.log("Listening on http://localhost:3000")
})
Ory checks for both the cookie and the token, which means that your API must handle both Ory Session Cookies and Ory Session Tokens:
// Copyright © 2026 Ory Corp
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
import "github.com/ory/client-go"
var ory *client.APIClient
func init() {
conf := client.NewConfiguration()
conf.Servers = client.ServerConfigurations{{URL: "https://" + os.Getenv("ORY_PROJECT_SLUG") + ".projects.oryapis.com"}}
ory = client.NewAPIClient(conf)
}
func main() {
fmt.Println("Listening on :3000")
err := http.ListenAndServe(":3000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := ""
if h := r.Header.Get("Authorization"); strings.HasPrefix(strings.ToLower(h), "bearer ") {
token = h[7:]
}
cookies := r.Header.Values("Cookie")
session, _, err := ory.FrontendAPI.ToSession(r.Context()).
XSessionToken(token).
Cookie(strings.Join(cookies, "; ")).
Execute()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(session)
}))
if err != nil {
panic(err)
}
}
import { Configuration, FrontendApi, Session } from "@ory/client"
import { useEffect, useState } from "react"
import * as SecureStore from "expo-secure-store"
const ory = new FrontendApi(
new Configuration({
basePath: `https://$PROJECT_SLUG.projects.oryapis.com`,
}),
)
async function getSessionTokenFromStore() {
return await SecureStore.getItemAsync("session_token")
}
export default async function SomeComponent() {
const [session, setSession] = useState<Session | undefined>()
const sessionToken = await getSessionTokenFromStore()
useEffect(() => {
if (!sessionToken) {
return
}
ory
.toSession({
xSessionToken: sessionToken,
})
.then(({ data }) => setSession(data))
}, [sessionToken])
return <div>{session?.id}</div>
}
Session response
Ory sessions have the format of:
{
"id": "65dea6f4-5d15-4e61-9eb7-f30190c0b2e2",
"active": true,
"expires_at": "2022-12-31T13:50:30.427292Z",
"authenticated_at": "2022-12-01T13:50:30.825516Z",
"authenticator_assurance_level": "aal1",
"authentication_methods": [
{
"method": "password",
"aal": "aal1",
"completed_at": "2022-12-01T13:50:30.427375604Z"
}
],
"issued_at": "2022-12-01T13:50:30.427292Z",
"identity": {
"id": "969d7a6e-b8a9-49ea-bf7b-9e2732a41a81",
"schema_id": "9cadbdf1d6bc5c5c521a1c17ea83648c911c5cd74a14d9e6cc11a5790d133339c3524f8a2d35d34f4151d2df10a7b73d19f7bd0f709fd5ace9019e080bbc4df6",
"state": "active",
"state_changed_at": "2022-12-01T13:50:30.331786Z",
"traits": {
"consent": {
"newsletter": false,
"tos": "2022-12-01T13:50:28.706Z"
},
"name": "User"
},
"verifiable_addresses": [
{
"id": "f5f4afde-697e-4e10-a6b6-f870dce927d6",
"verified": false,
"via": "email",
"status": "sent",
"created_at": "2022-12-01T13:50:30.345386Z",
"updated_at": "2022-12-01T13:50:30.345386Z"
}
],
"recovery_addresses": [
{
"id": "6e16a3b4-518f-4a5a-b045-95177deb40f1",
"via": "email",
"created_at": "2022-12-01T13:50:30.35227Z",
"updated_at": "2022-12-01T13:50:30.35227Z"
}
],
"metadata_public": null,
"created_at": "2022-12-01T13:50:30.340643Z",
"updated_at": "2022-12-01T13:50:30.340643Z"
},
"devices": [
{
"id": "592515ee-7b70-4e08-b4d7-fb0b1643cb48",
"ip_address": "2001:a61:1101:8001:cc85:1111:2222:3333",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
"location": "Berlin, DE"
}
]
}
Authentication methods
The authentication_methods array records every authenticator the user completed for this session, in the order they completed
them. Each entry names the credential type in method and the assurance level that method introduced in aal.
When the user signs in or registers with a one-time code, the entry also carries the channel the code was delivered over:
{
"authentication_methods": [
{
"method": "code",
"channel": "sms",
"aal": "aal1",
"completed_at": "2022-12-01T13:50:30.427375604Z"
}
]
}
channel is either email or sms, and Ory only sets it for the code method. Use it when the next step of your sign-in
experience depends on how the user authenticated. For example, you can ask a user who signed in with an SMS code for their email
address.