Redirect and callback protection
Exact callbacks and private browser-bound backend sessions protect parameter-free returns.
On this page
Register an exact callback
AnyOAuth redirects only to URLs on your project’s exact allowlist. HTTPS is required except for HTTP loopback during development. Credentials, queries, fragments, wildcards and ambiguous URL syntax are not callback registration features.
The API rechecks the allowlist before success and denial/error redirects. Removing a callback while sign-in is in progress prevents that transaction from returning to it.
Bind clean callbacks to the initiating browser
In the recommended flow, create a private random session key on your backend and register it with createLoginSession. Save it in a ten-minute server session selected by a Secure HttpOnly SameSite=Lax browser cookie. The launch URL uses a separate opaque ID; it does not disclose the private result key.
AnyOAuth returns to your registered callback without query parameters or a fragment. Require the saved browser cookie and use its original private key to call loginResult server-to-server. Reject query-bearing callbacks. Terminal results are retrieved once, with client credentials and exact callback binding. Only succeeded results with a valid profile can establish an application session.
Use one active pending login per browser. A callback must never select a transaction by guessing a key or searching all users’ pending records. Pending results are not proof of authentication. AnyOAuth still verifies provider state, nonce, PKCE and its own initiating browser cookie before completing the session.
Legacy flow: bind state to the initiating session
Create an unpredictable transaction and save it with the exact registered callback in the initiating browser’s server session or appropriate protected app storage.
import { AnyOAuth } from "@anyoauth/node";
const client = new AnyOAuth({
clientId: process.env.ANYOAUTH_CLIENT_ID ?? "YOUR_CLIENT_ID",
clientSecret: process.env.ANYOAUTH_CLIENT_SECRET ?? "YOUR_CLIENT_SECRET",
baseUrl: process.env.ANYOAUTH_API_ORIGIN ?? "https://api.anyoauth.com",
});
const transaction = await client.createTransaction();
@anyoauth/node on GitHubConfidential backend SDK
// Supply these public settings from your app configuration.
const settings = globalThis.ANYOAUTH_SETTINGS ?? {};
import { AnyOAuth } from "@anyoauth/client";
const client = new AnyOAuth({
clientId: settings.ANYOAUTH_CLIENT_ID ?? "YOUR_CLIENT_ID",
baseUrl: settings.ANYOAUTH_API_ORIGIN ?? "https://api.anyoauth.com",
});
const transaction = await client.createTransaction();
@anyoauth/client on GitHubClient helpers · backend exchange required
// Supply these public settings from your app configuration.
const settings = globalThis.ANYOAUTH_SETTINGS ?? {};
import { AnyOAuth } from "@anyoauth/expo/native";
import * as Crypto from 'expo-crypto';
const client = new AnyOAuth({
crypto: Crypto,
clientId: settings.ANYOAUTH_CLIENT_ID ?? "YOUR_CLIENT_ID",
baseUrl: settings.ANYOAUTH_API_ORIGIN ?? "https://api.anyoauth.com",
});
const transaction = await client.createTransaction();
@anyoauth/expo on GitHubClient helpers · backend exchange required
import os
from anyoauth import Client
client = Client(os.getenv("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), os.getenv("ANYOAUTH_CLIENT_SECRET", "YOUR_CLIENT_SECRET"), os.getenv("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
transaction = client.create_transaction()
anyoauth-sdk on GitHubConfidential backend SDK
package main
import (
"os"
anyoauth "github.com/AnyOAuth/anyoauth-go"
)
func env(key, fallback string) string { if v := os.Getenv(key); v != "" { return v }; return fallback }
func main() {
transaction, err := anyoauth.CreateTransaction()
if err != nil { panic(err) }
_ = transaction
}
github.com/AnyOAuth/anyoauth-go on GitHubConfidential backend SDK
<?php
require 'vendor/autoload.php';
$client = new \AnyOAuth\Client((getenv("ANYOAUTH_CLIENT_ID") ?: "YOUR_CLIENT_ID"), (getenv("ANYOAUTH_CLIENT_SECRET") ?: "YOUR_CLIENT_SECRET"), (getenv("ANYOAUTH_API_ORIGIN") ?: "https://api.anyoauth.com"));
$transaction = $client->createTransaction();
anyoauth/anyoauth on GitHubConfidential backend SDK
import com.anyoauth.AnyOAuth;
import com.google.gson.*;
public class Example {
static String env(String key, String fallback) { String value = System.getenv(key); return value == null ? fallback : value; }
public static void main(String[] args) {
var transaction = AnyOAuth.createTransaction();
}
}
com.anyoauth:anyoauth on GitHubConfidential backend SDK
require "anyoauth"
client = AnyOAuth::Client.new(ENV.fetch("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), ENV.fetch("ANYOAUTH_CLIENT_SECRET", "YOUR_CLIENT_SECRET"), ENV.fetch("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
transaction = client.create_transaction()
anyoauth-sdk on GitHubConfidential backend SDK
using System;
using System.Text.Json.Nodes;
using AnyOAuth;
var transaction = Client.CreateTransaction();
AnyOAuth.SDK on GitHubConfidential backend SDK
use anyoauth_sdk::*;
use serde_json::json;
fn env(key: &str, fallback: &str) -> String { std::env::var(key).unwrap_or_else(|_| fallback.to_owned()) }
fn main() -> Result<(), Box<dyn std::error::Error>> {
let transaction = create_transaction()?;
let _ = &transaction;
Ok(())
}
anyoauth-sdk on GitHubConfidential backend SDK
client = AnyOAuth.new((System.get_env("ANYOAUTH_CLIENT_ID") || "YOUR_CLIENT_ID"), (System.get_env("ANYOAUTH_CLIENT_SECRET") || "YOUR_CLIENT_SECRET"), (System.get_env("ANYOAUTH_API_ORIGIN") || "https://api.anyoauth.com"))
transaction = AnyOAuth.create_transaction(client)
anyoauth_sdk on GitHubConfidential backend SDK
import Foundation
import AnyOAuth
func env(_ key: String, _ fallback: String) -> String { ProcessInfo.processInfo.environment[key] ?? fallback }
let client = try AnyOAuth(clientId: env("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), baseUrl: env("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
let transaction = try client.createTransaction()
AnyOAuth on GitHubClient helpers · backend exchange required
import com.anyoauth.*
fun env(key: String, fallback: String): String = System.getenv(key) ?: fallback
fun main() {
val client = AnyOAuth(env("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), env("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
val transaction = client.createTransaction()
}
com.anyoauth:anyoauth-client on GitHubClient helpers · backend exchange required
import 'dart:io';
import 'package:anyoauth_client/anyoauth_client.dart';
String env(String key, String fallback) => Platform.environment[key] ?? fallback;
void main() {
final client = AnyOAuth(clientId: env("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), baseUrl: env("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"));
final transaction = client.createTransaction();
}
anyoauth_client on GitHubClient helpers · backend exchange required
Validate the returned state and expiry before using a code. Consume the saved transaction atomically, even on denial; the helper does not provide persistent session storage.
Legacy flow: keep the PKCE verifier
The S256 challenge is sent during authorization; the original verifier is sent only to the code-exchange endpoint from the confidential backend. A mismatched verifier cannot redeem the grant. Do not place the verifier or secret in a redirect URL.
import { AnyOAuth } from "@anyoauth/node";
const client = new AnyOAuth({
clientId: process.env.ANYOAUTH_CLIENT_ID ?? "YOUR_CLIENT_ID",
clientSecret: process.env.ANYOAUTH_CLIENT_SECRET ?? "YOUR_CLIENT_SECRET",
baseUrl: process.env.ANYOAUTH_API_ORIGIN ?? "https://api.anyoauth.com",
});
const challenge = await client.pkceChallenge({
codeVerifier: process.env.ANYOAUTH_CODE_VERIFIER ?? "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
});
@anyoauth/node on GitHubConfidential backend SDK
// Supply these public settings from your app configuration.
const settings = globalThis.ANYOAUTH_SETTINGS ?? {};
import { AnyOAuth } from "@anyoauth/client";
const client = new AnyOAuth({
clientId: settings.ANYOAUTH_CLIENT_ID ?? "YOUR_CLIENT_ID",
baseUrl: settings.ANYOAUTH_API_ORIGIN ?? "https://api.anyoauth.com",
});
const challenge = await client.pkceChallenge({
codeVerifier: settings.ANYOAUTH_CODE_VERIFIER ?? "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
});
@anyoauth/client on GitHubClient helpers · backend exchange required
// Supply these public settings from your app configuration.
const settings = globalThis.ANYOAUTH_SETTINGS ?? {};
import { AnyOAuth } from "@anyoauth/expo/native";
import * as Crypto from 'expo-crypto';
const client = new AnyOAuth({
crypto: Crypto,
clientId: settings.ANYOAUTH_CLIENT_ID ?? "YOUR_CLIENT_ID",
baseUrl: settings.ANYOAUTH_API_ORIGIN ?? "https://api.anyoauth.com",
});
const challenge = await client.pkceChallenge({
codeVerifier: settings.ANYOAUTH_CODE_VERIFIER ?? "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
});
@anyoauth/expo on GitHubClient helpers · backend exchange required
import os
from anyoauth import Client
client = Client(os.getenv("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), os.getenv("ANYOAUTH_CLIENT_SECRET", "YOUR_CLIENT_SECRET"), os.getenv("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
challenge = client.pkce_challenge({"codeVerifier": os.getenv("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")})
anyoauth-sdk on GitHubConfidential backend SDK
package main
import (
"os"
anyoauth "github.com/AnyOAuth/anyoauth-go"
)
func env(key, fallback string) string { if v := os.Getenv(key); v != "" { return v }; return fallback }
func main() {
challenge, err := anyoauth.PKCEChallenge(env("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"))
if err != nil { panic(err) }
_ = challenge
}
github.com/AnyOAuth/anyoauth-go on GitHubConfidential backend SDK
<?php
require 'vendor/autoload.php';
$client = new \AnyOAuth\Client((getenv("ANYOAUTH_CLIENT_ID") ?: "YOUR_CLIENT_ID"), (getenv("ANYOAUTH_CLIENT_SECRET") ?: "YOUR_CLIENT_SECRET"), (getenv("ANYOAUTH_API_ORIGIN") ?: "https://api.anyoauth.com"));
$challenge = $client->pkceChallenge(["codeVerifier" => (getenv("ANYOAUTH_CODE_VERIFIER") ?: "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")]);
anyoauth/anyoauth on GitHubConfidential backend SDK
import com.anyoauth.AnyOAuth;
import com.google.gson.*;
public class Example {
static String env(String key, String fallback) { String value = System.getenv(key); return value == null ? fallback : value; }
public static void main(String[] args) {
var challenge = AnyOAuth.pkceChallenge(env("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"));
}
}
com.anyoauth:anyoauth on GitHubConfidential backend SDK
require "anyoauth"
client = AnyOAuth::Client.new(ENV.fetch("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), ENV.fetch("ANYOAUTH_CLIENT_SECRET", "YOUR_CLIENT_SECRET"), ENV.fetch("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
challenge = client.pkce_challenge({"codeVerifier" => ENV.fetch("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")})
anyoauth-sdk on GitHubConfidential backend SDK
using System;
using System.Text.Json.Nodes;
using AnyOAuth;
var challenge = Client.PkceChallenge(Environment.GetEnvironmentVariable("ANYOAUTH_CODE_VERIFIER") ?? "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk");
AnyOAuth.SDK on GitHubConfidential backend SDK
use anyoauth_sdk::*;
use serde_json::json;
fn env(key: &str, fallback: &str) -> String { std::env::var(key).unwrap_or_else(|_| fallback.to_owned()) }
fn main() -> Result<(), Box<dyn std::error::Error>> {
let challenge = pkce_challenge(&env("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"))?;
let _ = &challenge;
Ok(())
}
anyoauth-sdk on GitHubConfidential backend SDK
client = AnyOAuth.new((System.get_env("ANYOAUTH_CLIENT_ID") || "YOUR_CLIENT_ID"), (System.get_env("ANYOAUTH_CLIENT_SECRET") || "YOUR_CLIENT_SECRET"), (System.get_env("ANYOAUTH_API_ORIGIN") || "https://api.anyoauth.com"))
challenge = AnyOAuth.pkce_challenge(client, %{"codeVerifier" => (System.get_env("ANYOAUTH_CODE_VERIFIER") || "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk")})
anyoauth_sdk on GitHubConfidential backend SDK
import Foundation
import AnyOAuth
func env(_ key: String, _ fallback: String) -> String { ProcessInfo.processInfo.environment[key] ?? fallback }
let client = try AnyOAuth(clientId: env("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), baseUrl: env("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
let challenge = try client.pkceChallenge(env("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"))
AnyOAuth on GitHubClient helpers · backend exchange required
import com.anyoauth.*
fun env(key: String, fallback: String): String = System.getenv(key) ?: fallback
fun main() {
val client = AnyOAuth(env("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), env("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"))
val challenge = client.pkceChallenge(env("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"))
}
com.anyoauth:anyoauth-client on GitHubClient helpers · backend exchange required
import 'dart:io';
import 'package:anyoauth_client/anyoauth_client.dart';
String env(String key, String fallback) => Platform.environment[key] ?? fallback;
void main() {
final client = AnyOAuth(clientId: env("ANYOAUTH_CLIENT_ID", "YOUR_CLIENT_ID"), baseUrl: env("ANYOAUTH_API_ORIGIN", "https://api.anyoauth.com"));
final challenge = client.pkceChallenge(env("ANYOAUTH_CODE_VERIFIER", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"));
}
anyoauth_client on GitHubClient helpers · backend exchange required
Legacy flow: reject ambiguous callbacks
The API and SDK helpers reject duplicate state/code/error parameters and simultaneous code/error values. SDK callbacks must match the saved redirect rather than a callback destination supplied by the visitor.
Protect the post-login return page
A destination such as /billing is separate from the OAuth callback. Save an allowlisted
internal destination in your application session. Reject absolute URLs and
protocol-relative paths such as //attacker.example; never blindly redirect to a raw
returnTo query parameter. The AnyOAuth dashboard uses a fixed configured return origin.