Atex.OAuth.Permission (atex v0.7.1)

View Source

Summary

Functions

Creates an account permission for controlling PDS account hosting details.

Creates a blob permission for uploading media files to PDS.

Creates an identity permission for controlling network identity.

Creates an include permission for referencing a permission set.

Creates a new permission struct from a permission scope string.

Parses an AT Protocol permission scope string into its components.

Creates a repo permission for write access to records in the account's public repository.

Creates an RPC permission for authenticated API requests to remote services.

Converts a permission struct back into its scope string representation.

Types

account_action()

@type account_action() :: :read | :manage

account_attr()

@type account_attr() :: :email | :repo

account_opt()

@type account_opt() ::
  {:attr, account_attr()} | {:action, account_action()} | as_string()

include_opt()

@type include_opt() :: {:aud, String.t()} | as_string()

repo_opt()

@type repo_opt() ::
  {:create, boolean()}
  | {:update, boolean()}
  | {:delete, boolean()}
  | as_string()

rpc_opt()

@type rpc_opt() :: {:aud, String.t()} | {:inherit_aud, boolean()} | as_string()

t()

@type t() :: %Atex.OAuth.Permission{
  parameters: [{String.t(), String.t()}],
  positional: String.t() | nil,
  resource: String.t()
}

t_tuple()

@type t_tuple() ::
  {resource :: String.t(), positional :: String.t() | nil,
   parameters :: [{String.t(), String.t()}]}

Functions

account(opts \\ [])

@spec account([account_opt()]) :: t() | String.t()

Creates an account permission for controlling PDS account hosting details.

Controls access to private account information such as email address and repository import capabilities. These permissions cannot be included in permission sets and must be requested directly by client apps.

See the AT Protocol documentation for more information.

Options

  • :attr (required) - A component of account configuration. Must be :email or :repo.
  • :action (optional) - Degree of control. Can be :read or :manage. Defaults to :read.
  • :as_string (optional) - If true (default), returns a scope string, otherwise returns a Permission struct.

If :as_string is true a scope string is returned, otherwise the underlying Permission struct is returned.

Examples

# Read account email (default action, as string)
iex> Atex.OAuth.Permission.account(attr: :email)
"account:email"

# Read account email (as struct)
iex> Atex.OAuth.Permission.account(attr: :email, as_string: false)
%Atex.OAuth.Permission{
  resource: "account",
  positional: "email",
  parameters: []
}

# Read account email (explicit action)
iex> Atex.OAuth.Permission.account(attr: :email, action: :read)
"account:email?action=read"

# Manage account email
iex> Atex.OAuth.Permission.account(attr: :email, action: :manage)
"account:email?action=manage"

# Import repo
iex> Atex.OAuth.Permission.account(attr: :repo, action: :manage)
"account:repo?action=manage"

blob(accept, opts \\ [])

@spec blob(String.t() | [String.t()], [as_string()]) :: t() | String.t()

Creates a blob permission for uploading media files to PDS.

Controls the ability to upload blobs (media files) to the PDS. Permissions can be restricted by MIME type patterns.

See the AT Protocol documentation for more information.

Parameters

  • accept - A single MIME type string or list of MIME type strings/patterns. Supports glob patterns like "*/*" or "video/*".
  • opts - Keyword list of options.

Options

  • :as_string (optional) - If true (default), returns a scope string, otherwise returns a Permission struct.

If :as_string is true a scope string is returned, otherwise the underlying Permission struct is returned.

Examples

# Upload any type of blob
iex> Atex.OAuth.Permission.blob("*/*")
"blob:*/*"

# Only images
iex> Atex.OAuth.Permission.blob("image/*", as_string: false)
%Atex.OAuth.Permission{
  resource: "blob",
  positional: "image/*",
  parameters: []
}

# Multiple mimetypes
iex> Atex.OAuth.Permission.blob(["video/*", "text/html"])
"blob?accept=video/*&accept=text/html"

# Multiple more specific mimetypes
iex> Atex.OAuth.Permission.blob(["image/png", "image/jpeg"], as_string: false)
%Atex.OAuth.Permission{
  resource: "blob",
  positional: nil,
  parameters: [{"accept", "image/png"}, {"accept", "image/jpeg"}]
}

identity(attr, opts \\ [])

@spec identity(:handle | :*, [as_string()]) :: t() | String.t()

Creates an identity permission for controlling network identity.

Controls access to the account's DID document and handle. Note that the PDS might not be able to facilitate identity changes if it does not have control over the DID document (e.g., when using did:web).

See the AT Protocol documentation for more information.

Parameters

  • attr - An aspect or component of identity. Must be :handle or :* (wildcard).
  • opts - Keyword list of options.

Options

  • :as_string (optional) - If true (default), returns a scope string, otherwise returns a Permission struct.

If :as_string is true a scope string is returned, otherwise the underlying Permission struct is returned.

Examples

# Update account handle (as string)
iex> Atex.OAuth.Permission.identity(:handle)
"identity:handle"

# Full identity control (as struct)
iex> Atex.OAuth.Permission.identity(:*, as_string: false)
%Atex.OAuth.Permission{
  resource: "identity",
  positional: "*",
  parameters: []
}

include(nsid, opts \\ [])

@spec include(String.t(), [include_opt()]) :: t() | String.t()

Creates an include permission for referencing a permission set.

Permission sets are Lexicon schemas that bundle together multiple permissions under a single NSID. This allows developers to request a group of related permissions with a single scope string, improving user experience by reducing the number of individual permissions that need to be reviewed.

The nsid parameter is required and must be a valid NSID that resolves to a permission set Lexicon schema. An optional aud parameter can be used to specify the audience for any RPC permissions within the set that have inheritAud: true.

See the AT Protocol documentation for more information.

Parameters

  • nsid - The NSID of the permission set (e.g., "com.example.authBasicFeatures")
  • opts - Keyword list of options.

Options

  • :aud (optional) - Audience of API requests as a DID service reference (e.g., "did:web:api.example.com#srvtype"). Supports wildcard ("*").
  • :as_string (optional) - If true (default), returns a scope string, otherwise returns a Permission struct.

If :as_string is true a scope string is returned, otherwise the underlying Permission struct is returned.

Examples

# Include a permission set (as string)
iex> Atex.OAuth.Permission.include("com.example.authBasicFeatures")
"include:com.example.authBasicFeatures"

# Include a permission set with audience (as struct)
iex> Atex.OAuth.Permission.include("com.example.authFull", aud: "did:web:api.example.com#svc_chat", as_string: false)
%Atex.OAuth.Permission{
  resource: "include",
  positional: "com.example.authFull",
  parameters: [{"aud", "did:web:api.example.com#svc_chat"}]
}

# Include a permission set with wildcard audience
iex> Atex.OAuth.Permission.include("app.example.authFull", aud: "*")
"include:app.example.authFull?aud=*"

new(string)

@spec new(String.t()) :: {:ok, t()} | {:error, reason :: atom()}

Creates a new permission struct from a permission scope string.

Parses an AT Protocol OAuth permission scope string and returns a structured representation. Permission strings follow the format resource:positional?key=value&key2=value2

The positional parameter is resource-specific and may be omitted in some cases (e.g., collection for repo, lxm for rpc, attr for account/identity, accept for blob).

See the AT Protocol documentation for the full syntax and rules for permission scope strings.

Parameters

  • string - A permission scope string (e.g., "repo:app.example.profile")

Returns {:ok, permission} if a valid scope string was given, otherwise it will return {:error, reason}.

Examples

# Simple with just a positional
iex> Atex.OAuth.Permission.new("repo:app.example.profile")
{:ok, %Atex.OAuth.Permission{
  resource: "repo",
  positional: "app.example.profile",
  parameters: []
}}

# With parameters
iex> Atex.OAuth.Permission.new("repo?collection=app.example.profile&collection=app.example.post")
{:ok, %Atex.OAuth.Permission{
  resource: "repo",
  positional: nil,
  parameters: [
    {"collection", "app.example.profile"},
    {"collection", "app.example.post"}
  ]
}}

# Positional with parameters
iex> Atex.OAuth.Permission.new("rpc:app.example.moderation.createReport?aud=*")
{:ok, %Atex.OAuth.Permission{
  resource: "rpc",
  positional: "app.example.moderation.createReport",
  parameters: [{"aud", "*"}]
}}

iex> Atex.OAuth.Permission.new("blob:*/*")
{:ok, %Atex.OAuth.Permission{
  resource: "blob",
  positional: "*/*",
  parameters: []
}}

# Invalid: resource without positional or parameters
iex> Atex.OAuth.Permission.new("resource")
{:error, :missing_positional_or_parameters}

parse(string)

@spec parse(String.t()) :: {:ok, t_tuple()} | {:error, reason :: atom()}

Parses an AT Protocol permission scope string into its components.

Returns a tuple containing the resource name, optional positional parameter, and a list of key-value parameter pairs. This is a lower-level function compared to new/1, returning the raw components instead of a struct.

Parameters

  • string - A permission scope string following the format resource:positional?key=value&key2=value2

Returns {:ok, {resource, positional, parameters}} if a valid scope string was given, otherwise it will return {:error, reason}.

Examples

# Simple with just a positional
iex> Atex.OAuth.Permission.parse("repo:app.example.profile")
{:ok, {"repo", "app.example.profile", []}}

# With parameters
iex> Atex.OAuth.Permission.parse("repo?collection=app.example.profile&collection=app.example.post")
{:ok, {
  "repo",
  nil,
  [
    {"collection", "app.example.profile"},
    {"collection", "app.example.post"}
  ]
}}

# Positional with parameters
iex> Atex.OAuth.Permission.parse("rpc:app.example.moderation.createReport?aud=*")
{:ok, {"rpc", "app.example.moderation.createReport", [{"aud", "*"}]}}

iex> Atex.OAuth.Permission.parse("blob:*/*")
{:ok, {"blob", "*/*", []}}

# Invalid: resource without positional or parameters
iex> Atex.OAuth.Permission.parse("resource")
{:error, :missing_positional_or_parameters}

repo(collection_or_collections, actions \\ [create: true, update: true, delete: true])

@spec repo(String.t() | [String.t()], [repo_opt()]) :: t() | String.t()

Creates a repo permission for write access to records in the account's public repository.

Controls write access to specific record types (collections) with optional restrictions on the types of operations allowed (create, update, delete).

When no options are provided, all operations are permitted. When any action option is explicitly set, only the actions set to true are enabled. This allows for precise control over permissions.

See the AT Protocol documentation for more information.

Parameters

  • collection_or_collections - A single collection NSID string or list of collection NSIDs. Use "*" for wildcard access to all record types (not allowed in permission sets).
  • options - Keyword list to restrict operations. If omitted, all operations are allowed. If any action is specified, only explicitly enabled actions are permitted.

Options

  • :create - Allow creating new records.
  • :update - Allow updating existing records.
  • :delete - Allow deleting records.
  • :as_string (optional) - If true (default), returns a scope string, otherwise returns a Permission struct.

If :as_string is true a scope string is returned, otherwise the underlying Permission struct is returned.

Examples

# Full permission on a single record type (all actions enabled, actions omitted)
iex> Atex.OAuth.Permission.repo("app.example.profile")
"repo:app.example.profile"

# Create only permission (other actions implicitly disabled)
iex> Atex.OAuth.Permission.repo("app.example.post", create: true, as_string: false)
%Atex.OAuth.Permission{
  resource: "repo",
  positional: "app.example.post",
  parameters: [{"action", "create"}]
}

# Delete only permission
iex> Atex.OAuth.Permission.repo("app.example.like", delete: true)
"repo:app.example.like?action=delete"

# Create and update only, delete implicitly disabled
iex> Atex.OAuth.Permission.repo("app.example.repost", create: true, update: true)
"repo:app.example.repost?action=update&action=create"

# Multiple collections with full permissions (no options provided, actions omitted)
iex> Atex.OAuth.Permission.repo(["app.example.profile", "app.example.post"])
"repo?collection=app.example.profile&collection=app.example.post"

# Multiple collections with only update permission (as struct)
iex> Atex.OAuth.Permission.repo(["app.example.like", "app.example.repost"], update: true, as_string: false)
%Atex.OAuth.Permission{
  resource: "repo",
  positional: nil,
  parameters: [
    {"collection", "app.example.like"},
    {"collection", "app.example.repost"},
    {"action", "update"}
  ]
}

# Wildcard permission (all record types, all actions enabled, actions omitted)
iex> Atex.OAuth.Permission.repo("*")
"repo:*"

rpc(lxm_or_lxms, opts \\ [])

@spec rpc(String.t() | [String.t()], [rpc_opt()]) :: t() | String.t()

Creates an RPC permission for authenticated API requests to remote services.

The permission is parameterised by the remote endpoint (lxm, short for "Lexicon Method") and the identity of the remote service (the audience, aud). Permissions must be restricted by at least one of these parameters.

See the AT Protocol documentation for more information.

Parameters

  • lxm - A single NSID string or list of NSID strings representing API endpoints. Use "*" for wildcard access to all endpoints.
  • opts - Keyword list of options.

Options

  • :aud (semi-required) - Audience of API requests as a DID service reference (e.g., "did:web:api.example.com#srvtype"). Supports wildcard ("*").
  • :inherit_aud (optional) - If true, the aud value will be inherited from permission set invocation context. Only used inside permission sets.
  • :as_string (optional) - If true (default), returns a scope string, otherwise returns a Permission struct.

Note

aud and lxm cannot both be wildcard. The permission must be restricted by at least one of them.

If :as_string is true a scope string is returned, otherwise the underlying Permission struct is returned.

Examples

# Single endpoint with wildcard audience (as string)
iex> Atex.OAuth.Permission.rpc("app.example.moderation.createReport", aud: "*")
"rpc:app.example.moderation.createReport?aud=*"

# Multiple endpoints with specific service (as struct)
iex> Atex.OAuth.Permission.rpc(
...>   ["app.example.getFeed", "app.example.getProfile"],
...>   aud: "did:web:api.example.com#svc_appview",
...>   as_string: false
...> )
%Atex.OAuth.Permission{
  resource: "rpc",
  positional: nil,
  parameters: [
    {"aud", "did:web:api.example.com#svc_appview"},
    {"lxm", "app.example.getFeed"},
    {"lxm", "app.example.getProfile"}
  ]
}

# Wildcard method with specific service
iex> Atex.OAuth.Permission.rpc("*", aud: "did:web:api.example.com#svc_appview")
"rpc:*?aud=did:web:api.example.com%23svc_appview"

# Single endpoint with inherited audience (for permission sets)
iex> Atex.OAuth.Permission.rpc("app.example.getPreferences", inherit_aud: true)
"rpc:app.example.getPreferences?inheritAud=true"

to_string(struct)

@spec to_string(t()) :: String.t()

Converts a permission struct back into its scope string representation.

This is the inverse operation of new/1, converting a structured permission back into the AT Protocol OAuth scope string format. The resulting string can be used directly as an OAuth scope parameter.

Values in parameters are automatically URL-encoded as needed (e.g., # becomes %23).

Parameters

  • struct - An %Atex.OAuth.Permission{} struct

Returns a permission scope string.

Examples

# Simple with just a positional
iex> perm = %Atex.OAuth.Permission{
...>   resource: "repo",
...>   positional: "app.example.profile",
...>   parameters: []
...> }
iex> Atex.OAuth.Permission.to_string(perm)
"repo:app.example.profile"

# With parameters
iex> perm = %Atex.OAuth.Permission{
...>   resource: "repo",
...>   positional: nil,
...>   parameters: [
...>     {"collection", "app.example.profile"},
...>     {"collection", "app.example.post"}
...>   ]
...> }
iex> Atex.OAuth.Permission.to_string(perm)
"repo?collection=app.example.profile&collection=app.example.post"

# Positional with parameters
iex> perm = %Atex.OAuth.Permission{
...>   resource: "rpc",
...>   positional: "app.example.moderation.createReport",
...>   parameters: [{"aud", "*"}]
...> }
iex> Atex.OAuth.Permission.to_string(perm)
"rpc:app.example.moderation.createReport?aud=*"

iex> perm = %Atex.OAuth.Permission{
...>   resource: "blob",
...>   positional: "*/*",
...>   parameters: []
...> }
iex> Atex.OAuth.Permission.to_string(perm)
"blob:*/*"

# Works via String.Chars protocol
iex> perm = %Atex.OAuth.Permission{
...>   resource: "account",
...>   positional: "email",
...>   parameters: []
...> }
iex> to_string(perm)
"account:email"