On This Page

OAuth

14 symbols from @neontechspace/fluxerly/effect

Variables#

oauth#

Create a server-side OAuth client, or generate an S256 PKCE challenge for an authorization request

The create method returns an Effect that requires Scope. Invalid configuration is misuse and dies with ConfigurationError. The application owns consent, callback correlation, state validation, token storage, installation policy and coordinated refresh

const oauth: Readonly<{ … }>
TypeScript
import { Effect } from "effect"
import { oauth, OAuthScopes } from "@neontechspace/fluxerly/effect"

export const oauthEffectExample = Effect.scoped(
    Effect.gen(function* () {
        const client = yield* oauth.create({ clientId: "123", clientSecret: "server-held-secret" })
        const pkce = oauth.createPkce()
        return yield* client.authorizationUrl({
            redirectUri: "https://app.example.test/oauth/callback",
            scopes: [OAuthScopes.Identify, OAuthScopes.Bot],
            state: "caller-correlated-state",
            codeChallenge: pkce.challenge,
            guildId: "456",
            permissions: 0n,
        })
    }),
)
create#

Create the OAuth client when this Effect executes, without making a request

Invalid client credentials or instance settings are misuse and die with ConfigurationError. Unexpected failures remain defects in the Cause. Scope closure calls shutdown

(config: OAuthConfig)
    => Effect.Effect<OAuthClient, never, Scope.Scope>(config: OAuthConfig)
  => Effect.Effect<
    OAuthClient, never, Scope.Scope
  >
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

Interfaces#

OAuthClient#

Use OAuth to access a user's Fluxer account with their consent and a server-held client secret

The scope that creates this client shuts it down

interface OAuthClient

The application handles consent, browser callbacks, state checks, token storage and refresh coordination

The client keeps a copy of the secret until shutdown and runs at most eight operations at once. While eight are running, another operation fails at once with OAuthOperationError reason busy instead of waiting. Response bodies are capped at 1 MiB. Requests never retry automatically. 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. Discovery throttling fails with OAuthOperationError reason rateLimit, outcome notDispatched, status 429 and the server's retryAfterMs when available

Expected failures use Effect's error channel. Interruption and cleanup defects remain in Cause. Response and discovery cleanup faults keep their original values as Cause defects, and describeError masks credential patterns in them

Operation input properties are read during execution, with throwing accessors retained as defects

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
): Effect<string, OAuthOperationFailure>authorizationUrl(
  input: OAuthAuthorizationInput,
  options?: RequestOptions
): Effect<
  string,
  OAuthOperationFailure
>

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
): Effect<OAuthTokens, OAuthOperationFailure>exchangeCode(
  input: OAuthCodeExchangeInput,
  options?: RequestOptions
): Effect<
  OAuthTokens,
  OAuthOperationFailure
>

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
): Effect<readonly OAuthConnection[], OAuthOperationFailure>fetchConnections(
  accessToken: string,
  options?: RequestOptions
): Effect<
  readonly OAuthConnection[],
  OAuthOperationFailure
>

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
): Effect<readonly GuildListSummary[], OAuthOperationFailure>fetchGuilds(
  accessToken: string,
  query?: GuildListQuery,
  options?: RequestOptions
): Effect<
  readonly GuildListSummary[],
  OAuthOperationFailure
>

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
): Effect<OAuthIdentity, OAuthOperationFailure>fetchIdentity(
  accessToken: string,
  options?: RequestOptions
): Effect<
  OAuthIdentity,
  OAuthOperationFailure
>

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
): Effect<OAuthIntrospection, OAuthOperationFailure>introspect(
  token: string,
  options?: RequestOptions
): Effect<
  OAuthIntrospection,
  OAuthOperationFailure
>

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
): Effect<OAuthTokens, OAuthOperationFailure>refresh(
  refreshToken: string,
  options?: RequestOptions
): Effect<
  OAuthTokens,
  OAuthOperationFailure
>

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
): Effect<void, OAuthOperationFailure>revoke(
  input: {
    readonly token: string
    readonly tokenTypeHint?:
      | "refresh_token"
      | "access_token"
  },
  options?: RequestOptions
): Effect<
  void,
  OAuthOperationFailure
>

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(): Effect<void>
Returns
Effect<void>

Those cleanup defects remain in the affected Effect's Cause

Re-Exports#

Declared in JavaScript & TypeScript and documented there. Import them from @neontechspace/fluxerly/effect as well

OAuthScopes#
Choose which account data the user may share, or request bot installation, in authorizationUrl
OAuthActiveIntrospection#
Token-check result when Fluxer reports this application's access or refresh token as active
OAuthAuthorizationInput#
Build the consent URL for an authorization-code request with PKCE
OAuthCodeExchangeInput#
Values received or retained by the application for exchangeCode after a consent callback
OAuthConfig#
Create a server-side OAuth client for exchanging authorization codes with an application secret
OAuthConnection#
One connection returned by fetchConnections with the connections scope, as an immutable value
OAuthIdentity#
Frozen basic account identity returned by fetchIdentity using a delegated access token
OAuthInactiveIntrospection#
Token-check result when Fluxer does not report the credential as active for this application
OAuthPkce#
A PKCE secret and its public hash challenge, used to prove that the code exchange belongs to the authorization request
OAuthTokens#
Credentials returned by a successful exchangeCode or refresh call
OAuthIntrospection#
Result of introspect, which explicitly asks Fluxer whether a token is active for this application
OAuthScope#
One supported authorization scope from OAuthScopes