Authentication

Proc calls are authenticated with a secret key or authorization. Secret keys provide full access to your account, while authorizations provide fine-grained access control to specific abilities that you define up-front.

Client libraries handle authentication for you when a connection is created. If you're not using a client library, you can pass the authorization through the authorization header with a type of bearer.

something went wrong :(
Creating an authenticated client
Setup
require "proc"

client = Proc.connect("PROCAUTH")
const Proc = require("@proc.dev/client");
const client = Proc.connect("PROCAUTH");
authorization=PROCAUTH
client.math.add.call(
  1, value: 1
)
client.math.add.call(
  1, {value: 1}
);
curl "https://proc.run/math/add?value=1" --silent \
--header "authorization: bearer $authorization" \
--header "content-type: application/json" \
--data '1'
 

2

Security Considerations

Secret keys provide unrestricted access to every proc endpoint. Exposing a secret key in any public way will allow someone to call any proc and access any persistent data in your account. Our recommendation is to only use secret keys to create authorizations with specific abilities.

Using Authorizations

Authorizations have no access by default and must be granted specific abilities at creation time. Their restrictive-by-default nature make limited authorizations safe to distribute in places they may become public, like in a web browser or mobile app. You can even configure authorizations to expire automatically, discussed more below.

Authorizations are created through the auth.create endpoint.

Defining Abilities

Authorizations only have access to the abilities they are created with. You can specify one or more abilities through the abilities argument in the auth.create proc. Each ability defines a package or proc that the authorization has access to. For example, creating an authorization with abilities of ["type"] grants access to all procs defined in the type package.

something went wrong :(
Creating an authorization for a package
Setup
require "proc"

client = Proc.connect("PROCAUTH")
const Proc = require("@proc.dev/client");
const client = Proc.connect("PROCAUTH");
authorization=PROCAUTH
authorization = client.auth.create.call(
  abilities: ["type"]
)

Proc.connect(authorization)["type.string.reverse"].call(
  "hello"
)
let authorization = await client.auth.create.call(
  null, {abilities: ["type"]}
);

await Proc.connect(authorization)["type.string.reverse"].call("hello");
authorization=$(curl https://proc.run/auth/create --silent \
--header "authorization: bearer $authorization" \
--header "content-type: application/vnd.proc+json" \
--header "accept: text/plain" \
--data '[["$$", "abilities", ["type"]]]')

curl "https://proc.run/type/string/reverse" --silent \
--header "authorization: bearer $authorization" \
--header "content-type: text/plain" \
--data 'hello'
 

olleh

Authorizations can be created with multiple abilities, providing fine-grained access control. For example, creating an authorization with abilities of ["keyv.get", "keyv.scan"] grants read-only access to the key-value store.

something went wrong :(
Creating an authorization with read-only key access
Setup
require "proc"

client = Proc.connect("PROCAUTH")
const Proc = require("@proc.dev/client");
const client = Proc.connect("PROCAUTH");
authorization=PROCAUTH
authorization = client.auth.create.call(
  abilities: ["keyv.get", "keyv.scan"]
)
let authorization = await client.auth.create.call(
  null, {abilities: ["keyv.get", "keyv.scan"]}
);
curl https://proc.run/auth/create --silent \
--header "authorization: bearer $authorization" \
--header "content-type: application/vnd.proc+json" \
--header "accept: text/plain" \
--data '[["$$", "abilities", ["keyv.get", "keyv.scan"]]]'

Proc returns an error if the authorization attempts to call a proc it has not been authorized to call.

something went wrong :(
Using an authorization outside of its ability
Setup
require "proc"

client = Proc.connect("PROCAUTH")
const Proc = require("@proc.dev/client");
const client = Proc.connect("PROCAUTH");
authorization=PROCAUTH
limited = client.auth.create.call(
  abilities: ["type"]
)

Proc.connect(limited)["keyv.get"].call(
  "some_key"
)
let limited = await client.auth.create.call(
  null, {abilities: ["type"]}
);

await Proc.connect(limited).keyv.get.call(
  "some_key"
);
limited=$(curl https://proc.run/auth/create --silent \
--header "authorization: bearer $authorization" \
--header "content-type: application/vnd.proc+json" \
--header "accept: text/plain" \
--data '[["$$", "abilities", ["type"]]]')

curl "https://proc.run/keyv/get" --silent \
--header "authorization: bearer $limited" \
--header "content-type: text/plain" \
--data 'some_key'
 

authorization does not have the ability to access proc keyv.get

Key-Value Store Capabilities

The keyv package allows abilities to be defined for specific buckets and prefixes—read more in the key-value docs.

Insecure Abilities

Some abilities are dangerous to grant to an authorization. Here are some examples:

  • Granting auth.create would allow the authorization to create new authorizations with any abilities. The newly created authorizations could access any part of your account.
  • Granting proc.update would allow the authorization to arbitrarily update stored procs, potentially injecting harmful behavior into your account.

By default, attempting to create an insecure authorization causes an error. You can override this behavior by passing the insecure argument with a value of true when calling auth.create.

Expiring Authorizations

Authorizations do not expire by default. You can create an authentication that expires at a future point in time by passing a timestamp through the expiry argument to auth.create.

something went wrong :(
Creating an authorization that expires at a point in time
Setup
require "proc"

client = Proc.connect("PROCAUTH")
const Proc = require("@proc.dev/client");
const client = Proc.connect("PROCAUTH");
authorization=PROCAUTH
authorization = client.auth.create.call(
  abilities: ["type"], expiry: 1933803200
)
let authorization = await client.auth.create.call(
  null, {abilities: ["type"], expiry: 1933803200}
);
curl https://proc.run/auth/create?expiry=1933803200 --silent \
--header "authorization: bearer $authorization" \
--header "content-type: application/vnd.proc+json" \
--header "accept: text/plain" \
--data '[["$$", "abilities", ["type"]]]'

To create an authorization that expires in some number of seconds, you can create an authorization with a ttl.

something went wrong :(
Creating an authorization that expires in 60 seconds
Setup
require "proc"

client = Proc.connect("PROCAUTH")
const Proc = require("@proc.dev/client");
const client = Proc.connect("PROCAUTH");
authorization=PROCAUTH
authorization = client.auth.create.call(
  abilities: ["type"], ttl: 60
)
let authorization = await client.auth.create.call(
  null, {abilities: ["type"], ttl: 60}
);
curl https://proc.run/auth/create?ttl=60 --silent \
--header "authorization: bearer $authorization" \
--header "content-type: application/vnd.proc+json" \
--header "accept: text/plain" \
--data '[["$$", "abilities", ["type"]]]'

Managing Secrets

Secret keys are managed through your Proc Account Settings. You can create secrets or roll existing secrets.

Rolling Secrets

For an extra layer of security, secrets should be rolled from time to time. When you decide to roll a secret you can choose whether it should be rolled immediately or at a future point in time.

Note that authorizations and cursors are tied to the secret they are created with—when a rolled secret expires, all authorizations and cursors created from the expired secret will become invalid.


Stuck? Want to chat about an idea? Join the community on Discord.