On This Page

OAuth

14 symbols from @neontechspace/fluxerly

Variables#

OAuthScopes#

Choose which account data the user may share, or request bot installation, in authorizationUrl

const OAuthScopes: Readonly<{ … }>
Bot#

Include bot installation in the consent request

"bot"
Connections#

Read the consenting account's connected accounts

"connections"
Email#

Request the consenting account's email fields

"email"
Guilds#

Read the consenting account's community memberships

"guilds"
Identify#

Read the consenting account's basic identity

"identify"

oauth#

Create confidential OAuth clients and generate PKCE values for authorization-code flows

Use these helpers on a trusted server where the client secret is not exposed to a browser

const oauth: Readonly<{ … }>

The application handles browser navigation, callbacks, state correlation, consent and token storage. It also decides installation policy and coordinates refreshes. The create method returns the client synchronously without making a request, and throws ConfigurationError for invalid settings. The createPkce method returns a new verifier and S256 challenge for the same authorization flow. PKCE links authorization to the code exchange using a private random verifier and its public SHA-256 hash challenge. Keep the verifier private and send only the challenge to authorizationUrl

import { oauth, OAuthScopes } from "@neontechspace/fluxerly"

export async function oauthExample(clientId, clientSecret, redirectUri, state) {
    const client = oauth.create({ clientId, clientSecret })
    try {
        const pkce = oauth.createPkce()
        return await client.authorizationUrl({
            redirectUri,
            scopes: [OAuthScopes.Identify, OAuthScopes.Bot],
            state,
            codeChallenge: pkce.challenge,
            guildId: "456",
            permissions: 0n,
        })
    }
    finally {
        await client.shutdown()
    }
}
import { oauth, OAuthScopes } from "@neontechspace/fluxerly"

export async function oauthExample(clientId: string, clientSecret: string, redirectUri: string, state: string) {
    const client = oauth.create({ clientId, clientSecret })
    try {
        const pkce = oauth.createPkce()
        return await client.authorizationUrl({
            redirectUri,
            scopes: [OAuthScopes.Identify, OAuthScopes.Bot],
            state,
            codeChallenge: pkce.challenge,
            guildId: "456",
            permissions: 0n,
        })
    } finally {
        await client.shutdown()
    }
}
createPkce#

Generate a private verifier and matching S256 challenge for one authorization-code flow

Send challenge to authorizationUrl and keep verifier privately for exchangeCode. This synchronous helper returns the values immediately, without a request or token storage. It does not generate or verify the callback's state value

() => OAuthPkce

create#

Create a standalone OAuth client with a clientId and server-held clientSecret

Creation checks configuration synchronously without requests and copies the secret until shutdown. Invalid settings throw ConfigurationError. Unexpected creation failures throw SdkDefect with operation oauth.create and without configuration details. A throwing configuration getter uses code application.defect with the thrown value as its cause

create(config: OAuthConfig): OAuthClientcreate(
  config: OAuthConfig
): OAuthClient

Parameters

Interfaces#

OAuthActiveIntrospection#

Token-check result when Fluxer reports this application's access or refresh token as active

Inspect tokenType before using optional access-token expiry data

interface OAuthActiveIntrospection
active#

Fluxer reported this token as active at the time of the request, not guaranteed for later use

true
clientId#

Decimal ID of the application to which the token belongs

string
expiresAtUnixSeconds optional#

Provider-reported Unix seconds for an access token

Refresh-token results omit this field

number
issuedAtUnixSeconds#

Provider-reported Unix seconds when this token was issued

number
scopes#

Granted scopes parsed from Fluxer's space-separated response, with no authorization decision or retention

readonly string[]
subjectId optional#

Subject account ID when Fluxer includes one

Its absence does not make an otherwise active access token inactive

string
tokenType#

Whether the inspected credential is an access token or refresh token

"Bearer" | "refresh_token"

OAuthAuthorizationInput#

Build the consent URL for an authorization-code request with PKCE

The application keeps state and the PKCE verifier. When the callback arrives, it checks that state matches the sent value. The SDK does not save these values, open a browser or handle the redirect. Values are sampled once when authorizationUrl starts, then validated and encoded from that snapshot

interface OAuthAuthorizationInput
channelId optional#

Optional group-DM installation target for the bot scope

Mutually exclusive with guildId and not proof of installation

string
codeChallenge#

Public hash challenge produced by oauth.createPkce, using S256 and exactly 43 base64url characters

Retain the paired secret verifier for exchangeCode when the callback arrives

string
disableGuildSelect optional#

Ask the bot-installation consent page to hide community selection

This does not establish a selected target

boolean
guildId optional#

Optional community installation target for the bot scope

This hint neither proves membership nor grants permissions

string
permissions optional#

Optional unsigned 64-bit bot permission bitfield, serialized as a decimal provider parameter without local authorization

bigint
redirectUri#

Exact registered redirect, using HTTPS or loopback HTTP, without embedded credentials or a fragment

Must contain 1–256 well-formed UTF-16 units, with no surrounding whitespace, U+000C or U+202E. Noncanonical values are rejected rather than normalized

string
scopes#

Provide 1–256 delegated or bot-installation scopes from OAuthScopes

Supplied entries, including duplicates, count toward this SDK defensive limit. Duplicates are removed in first-occurrence order before serialization

readonly OAuthScope[]
state#

Unpredictable caller-generated value of 1–256 well-formed UTF-16 units, verified by the application on callback

Surrounding whitespace, U+000C and U+202E are rejected, not removed, so accepted state is preserved exactly

string

OAuthClient#

Use a standalone OAuth client to exchange authorization codes, refresh or revoke tokens, and read delegated user data

This confidential client requires a server-held client secret, not a bot token. It does not open a browser, handle callbacks, compare state, store tokens or run a gateway. The application owns those steps and coordinates refreshes

interface OAuthClient extends AsyncDisposableinterface OAuthClient
  extends AsyncDisposable

The secret is copied until shutdown. Calls start immediately and return ResultAsync. At most eight operations run at once. While eight are running, another operation fails at once with OAuthOperationError reason busy instead of waiting. The timeoutMs option defaults to 30,000 for discovery and the operation, with request cleanup awaited afterward. Malformed options fail with a local input error before discovery. Responses are capped at 1 MiB and requests are never retried automatically. Discovery throttling returns OAuthOperationError with reason rateLimit, outcome notDispatched, status 429 and the server's retryAfterMs when available

Expected failures return Err. A throw while reading input or option properties rejects with SdkDefect code application.defect and the thrown value as its cause, and unexpected cleanup failures reject with SdkDefect. It keeps the original cleanup failure, never response text, and its message and toJSON mask credential patterns

[asyncDispose]#

Shut down this client and wait for cleanup, as await using does at the end of a block

Unexpected cleanup failures reject with SdkDefect, as shutdown does

[asyncDispose](): Promise<void>
Returns
Promise<void>

authorizationUrl#

Build an authorization URL for the selected instance using its discovered web application URL, including its path

Supply the redirect URI, scopes, caller-generated state and S256 PKCE challenge, which binds the callback code to the private verifier. The application must retain state correlation and the PKCE verifier. Bot community and permission parameters are consent hints, not proof of installation or authorization. This returns the URL without opening it. Network failures retain only sanitized transport facts in cause

authorizationUrl(
    input: OAuthAuthorizationInput,
    options?: RequestOptions
): ResultAsync<
    string,
    ConfigurationError | OAuthOperationFailure | CancelledError
>authorizationUrl(
  input: OAuthAuthorizationInput,
  options?: RequestOptions
): ResultAsync<
  string,
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

options optional
RequestOptions

exchangeCode#

Exchange the code from an authorization callback for access and refresh tokens

Supply the matching redirect URI and PKCE verifier. The application must store the returned tokens. After dispatch, cancellation or a lost response cannot tell whether Fluxer consumed the one-use code. Do not retry that exchange when its outcome is unknown. Network failures retain only sanitized transport facts in cause

exchangeCode(
    input: OAuthCodeExchangeInput,
    options?: RequestOptions
): ResultAsync<
    OAuthTokens,
    ConfigurationError | OAuthOperationFailure | CancelledError
>exchangeCode(
  input: OAuthCodeExchangeInput,
  options?: RequestOptions
): ResultAsync<
  OAuthTokens,
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

options optional
RequestOptions

fetchConnections#

Fetch the delegated user's full connections list with a connections-scoped access token

The client neither creates, verifies, reorders nor retains those connections. Failures identify a read in details.read for errors.isRetryable. Requests are not retried automatically, and network causes retain only sanitized transport facts

fetchConnections(
    accessToken: string,
    options?: RequestOptions
): ResultAsync<
    readonly OAuthConnection[],
    ConfigurationError | OAuthOperationFailure | CancelledError
>fetchConnections(
  accessToken: string,
  options?: RequestOptions
): ResultAsync<
  readonly OAuthConnection[],
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

accessToken
string
options optional
RequestOptions

fetchGuilds#

Fetch one bounded page of the delegated user's community memberships

Use an access token with Fluxer's guilds scope. Optionally supply pagination settings in GuildListQuery, where limit defaults to 200 and accepts 1–200, and before and after are mutually exclusive cursors. This uses bearer authentication with the supplied access token, never the bot token. Failures identify a read in details.read for errors.isRetryable. Requests are not retried automatically, and network causes retain only sanitized transport facts

fetchGuilds(
    accessToken: string,
    query?: GuildListQuery,
    options?: RequestOptions
): ResultAsync<
    readonly GuildListSummary[],
    ConfigurationError | OAuthOperationFailure | CancelledError
>fetchGuilds(
  accessToken: string,
  query?: GuildListQuery,
  options?: RequestOptions
): ResultAsync<
  readonly GuildListSummary[],
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

accessToken
string
query optional
GuildListQuery
options optional
RequestOptions

fetchIdentity#

Fetch the delegated user's identity with an access token granted the identify scope

Additional identity fields depend on the token's scopes. The access token is not retained by this client. Failures identify a read in details.read, so errors.isRetryable can classify transient failures without implying a token change. Requests are not retried automatically, and network causes retain only sanitized transport facts

fetchIdentity(
    accessToken: string,
    options?: RequestOptions
): ResultAsync<
    OAuthIdentity,
    ConfigurationError | OAuthOperationFailure | CancelledError
>fetchIdentity(
  accessToken: string,
  options?: RequestOptions
): ResultAsync<
  OAuthIdentity,
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

accessToken
string
options optional
RequestOptions

introspect#

Inspect an access or refresh token using this client's ID and secret through HTTP Basic authentication

An inactive result does not explain expiry or revocation, prove token ownership or establish whether it ever existed. Failures identify a read in details.read for errors.isRetryable. Requests are not retried automatically, and network causes retain only sanitized transport facts

introspect(token: string, options?: RequestOptions): ResultAsync<
    OAuthIntrospection,
    ConfigurationError | OAuthOperationFailure | CancelledError
>introspect(
  token: string,
  options?: RequestOptions
): ResultAsync<
  OAuthIntrospection,
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

token
string
options optional
RequestOptions

refresh#

Exchange a refresh token for a new access and refresh token pair

Form tokens must contain 1–256 well-formed UTF-16 units without surrounding whitespace, U+000C or U+202E. Invalid values fail locally rather than being normalized, and this also applies to revoke and introspect tokens. Fluxer rotates refresh tokens. The application must coordinate refreshes and atomically replace both stored tokens after success, so it never stores a mixed pair. A refresh with an unknown outcome must not be retried, because it may have rotated the token. Network failures retain only sanitized transport facts in cause

refresh(
    refreshToken: string,
    options?: RequestOptions
): ResultAsync<
    OAuthTokens,
    ConfigurationError | OAuthOperationFailure | CancelledError
>refresh(
  refreshToken: string,
  options?: RequestOptions
): ResultAsync<
  OAuthTokens,
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

refreshToken
string
options optional
RequestOptions

revoke#

Revoke an access or refresh token, with an optional token-type hint

The token and hint are captured once when execution starts, then validated and transmitted from that snapshot. A lost response can still mean the token was revoked. Network failures retain only sanitized transport facts in cause

revoke(
    input: {
        readonly token: string
        readonly tokenTypeHint?: "refresh_token" | "access_token"
    },
    options?: RequestOptions
): ResultAsync<
    void,
    ConfigurationError | OAuthOperationFailure | CancelledError
>revoke(
  input: {
    readonly token: string
    readonly tokenTypeHint?:
      | "refresh_token"
      | "access_token"
  },
  options?: RequestOptions
): ResultAsync<
  void,
  | ConfigurationError
  | OAuthOperationFailure
  | CancelledError
>

Parameters

input
token

Access or refresh token to invalidate

Keep this secret out of logs

string
tokenTypeHint optional

Identify the token as an access token or a refresh token

Omit when the kind is unknown

"refresh_token" | "access_token"
options optional
RequestOptions

shutdown#

Permanently stop OAuth work, reject new work, release the client's copied secret and abort active requests

Shutdown waits for fetch and response-reader cleanup. A response-cleanup failure in an active operation fails that operation, while shutdown waits for it and may itself succeed. A failure in shutdown's own discovery cleanup fails shutdown

shutdown(): ResultAsync<void, never>
Returns
ResultAsync<void, never>

Those cleanup failures reject with SdkDefect

OAuthCodeExchangeInput#

Values received or retained by the application for exchangeCode after a consent callback

Verify the callback's state before exchanging its code, since the SDK does not perform that check. Values are sampled once when exchangeCode starts, then validated and transmitted from that snapshot

interface OAuthCodeExchangeInput
code#

Callback code of 1–256 well-formed UTF-16 units to exchange for tokens

Surrounding whitespace, U+000C and U+202E are rejected rather than changing the opaque credential. A dispatched attempt may consume this one-use code even if its response is lost

string
codeVerifier#

Original secret PKCE verifier, such as the verifier returned by oauth.createPkce

Must contain 43–128 ASCII letters, digits or the characters - . _ ~

string
redirectUri#

The exact redirect URI used in the authorization request, with the same 1–256-unit and canonical-text requirements

string

OAuthConfig#

Create a server-side OAuth client for exchanging authorization codes with an application secret

Keep the secret on the application server, not in browser code. This client is separate from a bot-token client. The application handles the consent callback and stores tokens

interface OAuthConfig
clientId#

Decimal Fluxer application ID

string
clientSecret#

Application client secret retained until shutdown

Shutdown releases the SDK's reference, not caller-held copies or the string's underlying memory

string
instance optional#

Omit for hosted Fluxer

A supplied instance requires its root URL and trusts its discovered service origins

InstanceOptions
logging optional#

Log records for this OAuth client, with the same settings as the client option logging

Omit it for the default console output of Info and above. A rejected client ID or secret logs one rest.rejected Warn record per operation and Fluxer code, even when the application handles the failure. A rejected user access token is only returned

LoggingOptions

OAuthConnection#

One connection returned by fetchConnections with the connections scope, as an immutable value

interface OAuthConnection
id#

Provider connection identifier, which may be empty and is not necessarily a decimal account ID

string
name#

Provider connection name, which may be empty

string
sortOrder#

Provider sort-order value, preserved without reordering the returned list

number
type#

Connection kind accepted by this SDK's response parser, currently bsky or domain

string
verified#

Whether Fluxer reports that the connection is verified

boolean
visibilityFlags#

Provider visibility bitfield, preserved as a nonnegative 32-bit integer without interpreting its flags

number

OAuthIdentity#

Frozen basic account identity returned by fetchIdentity using a delegated access token

Optional email fields are included only when Fluxer supplies them, not synthesized from requested scopes

interface OAuthIdentity
avatar#

Avatar hash, or null when the account has no avatar

string | null
discriminator#

Provider discriminator string, preserved without numeric conversion

string
displayName#

Account-wide display name, or null when absent

The display.name helper reads it

string | null
email optional#

Email address when supplied, null when explicitly absent, or an omitted field when not returned

string | null
id#

Decimal account ID

string
username#

Account username

string
verified optional#

Provider verification flag when supplied, null when unspecified, or an omitted field when not returned

boolean | null

OAuthInactiveIntrospection#

Token-check result when Fluxer does not report the credential as active for this application

This does not distinguish an unknown, expired or revoked token from a token issued to another client

interface OAuthInactiveIntrospection
active#

Fluxer did not report this token as active for this application

false

OAuthPkce#

A PKCE secret and its public hash challenge, used to prove that the code exchange belongs to the authorization request

The application owns these values, and the SDK does not retain them

interface OAuthPkce
challenge#

Public S256 challenge to pass to authorizationUrl

string
verifier#

Secret to retain for exchangeCode, not to publish in the consent URL or logs

string

OAuthTokens#

Credentials returned by a successful exchangeCode or refresh call

Store and protect these values in the application, since the SDK does not retain them or schedule refreshes. After a refresh, use the new credentials. The previous refresh token may no longer work

interface OAuthTokens
accessToken#

Bearer credential for delegated reads, not a bot token

string
expiresInSeconds#

Provider-reported lifetime in seconds, not an absolute expiry or a scheduled refresh

number
refreshToken#

Credential for an explicit refresh call, which may rotate it

string
scopes#

Granted scopes as returned by Fluxer

They can differ from requested scopes

readonly string[]
tokenType#

Authentication scheme for the access token

"Bearer"

Type Aliases#

OAuthScope#

One supported authorization scope from OAuthScopes

Returned token scopes remain strings because Fluxer can grant additional scopes unknown to this SDK

type OAuthScope = typeof OAuthScopes[keyof typeof OAuthScopes]type OAuthScope =
    typeof OAuthScopes[
      keyof typeof OAuthScopes
    ]