Skip to content

Ask a person to approve a sign-in

POST
/v1/bc-authorize
curl --request POST \
--url https://api.trybage.com/v1/bc-authorize \
--header 'Content-Type: application/json' \
--data '{ "client_assertion": "eyJhbGciOiJFZERTQSIsInR5cCI6ImNsaWVudC1hc3NlcnRpb24rand0In0...", "login_hint": "alex@example.com", "client_notification_token": "example", "audience": "https://shop.example", "scope": "browse cart", "binding_message": "Reorder this week'\''s groceries", "bind_key_proof": "example", "platform_attestation": "example" }'

Starts a sign-in request. The person gets a push on the Bage iPhone app, or an email with a link, and chooses Allow once, Always or Deny. If they chose Always before for this agent, site and scopes, the request is approved at once.

A website that verified its domain in the console may limit scopes (invalid_scope) and agents (site_refused); those are checked before anyone is asked.

Media typeapplication/json
object
client_assertion
required

A one-time JWT signed with the agent’s private key (RFC 7523). See the description at the top.

string
login_hint
required

The person’s email address (they don’t need a Bage account yet), or the account_link this agent got from an earlier sign-in with them. A link only works for the agent it was given to, and stops working when the person cuts the agent off (unknown_user_id).

string
client_notification_token

Ping mode only, and then required: a random secret (at least 128 bits) Bage sends back as the bearer token when it calls the agent’s notification endpoint about this request.

string
>= 16 characters <= 1024 characters
audience
required

The website’s origin, as its /.well-known/agent-login names it.

string format: uri
scope
required

Space-separated scopes - standard ones (the Scope schema) or the site’s own (custom_scopes in GET /v1/sites). A verified site allows email plus what it turned on.

string
binding_message

Shown to the person on the approval screen.

string
<= 140 characters
bind_key_proof

Bind the token to a per-session Ed25519 key instead of the agent’s own (for a browser extension that signs inside the browser). A JWT signed by that key: header typ: "key-binding+jwt" and jwk (the public key), claims iss = the client ID, aud = the issuer, exp at most 5 minutes out.

string
platform_attestation

Bind the token to a browser platform’s key, so the platform signs requests. A JWT from the platform, signed with its registered key: header typ: "platform-key+jwt", claims iss = the platform’s client ID, sub = this agent’s client ID, aud = the issuer, cnf.jwk = the browser’s public key, exp at most a day out. The token then names the platform in a nested act.

string

The request was created.

Media typeapplication/json
object
auth_req_id
required
string
expires_in
required

Seconds until the request expires.

integer
interval
required

Seconds to wait between polls.

integer

Example

{
"auth_req_id": "req_3f9c2b1a",
"expires_in": 300,
"interval": 2
}

invalid_request, invalid_scope, invalid_binding (a bad key proof or platform attestation), unknown_user_id (an account link this agent doesn’t have), or a test person asked by a live agent.

Media typeapplication/json
object
error
required
string
error_description
string

Example

{
"error": "authorization_pending"
}

invalid_client: unknown client, or a bad, expired or reused client assertion.

Media typeapplication/json
object
error
required
string
error_description
string

Example

{
"error": "authorization_pending"
}

site_refused: the website blocked this agent, or only lets in agents it allowed. test_mode: a test agent asked someone outside its team and the test people.

Media typeapplication/json
object
error
required
string
error_description
string

Example

{
"error": "authorization_pending"
}

rate_limited: too many requests, or too many to people who haven’t set up Bage.

Media typeapplication/json
object
error
required
string
error_description
string

Example

{
"error": "authorization_pending"
}