Skip to main content
The Auth0 Guard is the on-chain contract that proves an Auth0 login is authentic before NEAR Auth ever asks the MPC network to sign. It verifies the RS256 signature on an Auth0-issued JWT, checks the standard claims, and — most importantly — confirms the JWT was minted for this exact deployment and this exact transaction. It is deployed on mainnet at auth0.jwt.fast-auth.near and on testnet at auth0.jwt.fast-auth.testnet. It is the reference implementation of the JwtGuard trait and the default guard for the Auth0 provider. The JWT Guard Router routes verification requests to it under the guard name jwt#auth0.
The guard only verifies. It never holds keys or produces signatures. When verification succeeds it returns the JWT’s sub claim, which the NEAR Auth contract uses to derive the per-user MPC signing path.

What it verifies

Every call to verify runs the full JwtGuard pipeline, then layers Auth0-specific custom-claim checks on top. All of the following must pass, or verification fails:

RS256 signature

The JWT signature is verified with RSA PKCS#1 v1.5 (SHA-256) against the guard’s stored 2048-bit public keys.

Issuer (iss)

The token’s iss claim must match the issuer argument passed by the caller.

Expiry & not-before

exp must be in the future and nbf (if present) must already be in effect at the current block time.

Audience (aud)

aud must contain the guard’s own account id — the account the contract is deployed to.

Transaction binding (fatxn)

The custom fatxn claim must be byte-for-byte equal to the sign_payload passed in.

Size

The JWT is bounded (max 7 KB) before any parsing or cryptography runs.

The two custom claims

The base JwtGuard trait handles the signature and the standard iss / exp / nbf claims. The Auth0 Guard adds its own verify_custom_claims step, which deserializes the JWT payload into this structure:
custom_claims.rs
pub struct CustomClaims {
    pub fatxn: Vec<u8>,       // the encoded transaction payload
    pub aud: serde_json::Value, // string or array, per the OIDC spec
}
It then enforces two independent binding checks. Audience must contain the guard’s own account id. Per the OIDC spec, aud may be a single string or an array of strings, so the guard accepts either form and looks for its own env::current_account_id() among them:
verify_custom_claims.rs
let expected = env::current_account_id().to_string();
if !audience_matches(&claims.aud, &expected) {
    return (false, "audience mismatch".to_string());
}
This prevents a JWT minted for one deployment from being replayed against another — a token targeting the testnet guard cannot be used against mainnet, and vice versa. fatxn must equal the sign_payload. The fatxn claim is the encoded transaction or delegate-action payload the user authorized at login time. The guard requires it to match the sign_payload the caller is asking to sign, exactly:
verify_custom_claims.rs
if claims.fatxn != sign_payload {
    return (false, "Transaction payload mismatch".to_string());
}
The fatxn binding is what makes NEAR Auth safe against a compromised relayer or frontend. A valid JWT can only ever authorize the one transaction it was issued for — a signature for any other payload is impossible, because the guard rejects the request before the MPC network is ever consulted.
If both binding checks pass, verify_custom_claims returns success and the guard returns the token’s sub claim to the caller.

Methods

verify

The main entry point, called by the NEAR Auth contract or the JWT Guard Router during a signing flow. It is a read-only method (no state change) and runs the full verification pipeline described above.
pub fn verify(
    &self,
    issuer: String,
    jwt: String,
    sign_payload: Vec<u8>,
    predecessor: AccountId,
) -> (bool, String)
ParameterTypeDescription
issuerStringThe expected JWT issuer, e.g. https://login.testnet.fast-auth.com/. Must equal the token’s iss claim.
jwtStringThe full Auth0-issued JWT to verify.
sign_payloadVec<u8>The encoded transaction payload; must equal the token’s fatxn claim.
predecessorAccountIdThe original caller’s account id, forwarded through the routing chain.
Returns a (bool, String) tuple: the boolean is true when the JWT is valid, and the string carries the sub claim on success or an error message ("audience mismatch", "Transaction payload mismatch", and so on) on failure.

set_public_keys

Replaces the guard’s stored RSA public keys. Auth0’s signing keys rotate, so the owner keeps the guard’s key set current; the same method is used for emergency rotation after a suspected key compromise.
pub fn set_public_keys(&mut self, public_keys: Vec<JwtPublicKey>)
Each key is a modulus/exponent pair, and every key is validated before it is stored:
JwtPublicKey.rs
pub struct JwtPublicKey {
    pub n: Vec<u8>, // RSA modulus
    pub e: Vec<u8>, // RSA exponent
}
set_public_keys is owner-only. Any call from an account other than the stored owner panics with "Only the owner can call this function". The guard’s keys are owner-managed — this is the key distinction from the DAO- and attestation-managed guards described under Advanced.
The guard supports multiple public keys at once, so a signature verifies if it matches any stored key. That lets an operator publish the next key before the old one is retired, avoiding a verification gap during rotation.
The guard also exposes standard owner-management and upgrade methods, all owner-gated:
  • owner() — returns the current owner AccountId.
  • change_owner(new_owner) — transfers ownership; only the current owner may call it.
  • update_contract() — deploys new contract code to the guard account and runs a migrate state migration.
On initialization, init(owner, public_keys) sets the owner and validates every supplied public key before storing it.

How it fits the signing flow

The guard is invoked in the middle of a NEAR Auth signature request — it is never called directly by the SDK.
1

A JWT is minted with fatxn and aud

The user authenticates through the Auth0 provider, which embeds the encoded transaction in the fatxn claim and sets aud to the target guard’s account id.
2

The app calls FastAuth.sign

The frontend calls sign(guard_id, jwt, sign_payload, algorithm) on the NEAR Auth contract with guard_id: "jwt#auth0".
3

The router forwards to this guard

The JWT Guard Router resolves jwt#auth0 to auth0.jwt.fast-auth.near (or .testnet) and calls verify.
4

The guard verifies everything

RS256 signature, iss, exp/nbf, aud contains this account, and fatxn == sign_payload. On success it returns the sub claim.
5

MPC signs at the derived path

NEAR Auth uses the returned sub to derive the deterministic MPC path and requests the signature. The verified transaction is signed and can be submitted, optionally gasless via a relayer.

JWTs in NEAR Auth

How the token is structured, the fatxn claim, and on-chain RS256 verification.

The Auth0 provider

How Auth0 mints the JWT and sets the signing audience for this guard.

JWT Guard Router

The registry that maps jwt#auth0 to this guard’s account.

NEAR Auth contract

The entry point that calls into the guard during a signing flow.