Skip to main content
By default the SDK reads the site’s API key from .env. You can keep it in your app’s database instead, encrypted, and replace it from the terminal, with no redeploy and no .env edit. That suits an app where the key is rotated now and then, or where the person who holds the key isn’t the person who deploys the app.

Encrypted at rest

The key and its signing secret are encrypted with your app’s own APP_KEY, and never appear in toArray(), JSON or a dump.

Rotated in seconds

Swap in a new key from the CMS and the old one is gone from your app.

Same calls

Attract::blogPosts() and every other call work as before, signed as the key asks.

What the package gives you

You write no table, model or lookup code. The package ships all of it:

The migration

Creates the attract_api_keys table, one row per site. It’s published into your app’s database/migrations/ like any package migration.

The model

Fetchski\AttractCmsLaravel\Models\AttractApiKey, inside the package. It encrypts the key and secret, hides them from toArray() and JSON, and turns its row into the site the SDK calls.

The lookup

One line in .env switches the SDK from config/fetchski-attract.php to the table.

Before you start

The package is installed

From your own site, with any of its API keys: see Installation.

Your app has a database connection

Any database Laravel supports: the one your app already uses. The key is encrypted with your app’s existing APP_KEY, which nothing here changes.

You have an API key from the site

In the site’s CMS, API Keys → New API Key shows three things, once. Keep the page open until you’ve stored them:
  • the API key, fsk_acme_…
  • its signing secret, fss_… (none for a token-only key)
  • its signing mode: the key’s Request signing setting, on its Security tab

Set it up

Run:
Terminal
In order, it:
  1. publishes config/fetchski-attract.php and the migration, database/migrations/<date>_create_attract_api_keys_table.php;
  2. writes FETCHSKI_ATTRACT_SITES_FROM=database to .env (and an empty one to .env.example). It never writes the key to either;
  3. asks Run the migrations now? before it runs your app’s migrations;
  4. asks for the key (the prompts), stores it, and checks it on the site.
Answer no at step 3 and it stops there, so you can review the migration first. When you’re ready, run your migrations:
Terminal
Then store the key:
Terminal
In a deploy script, skip the migration question with --force:
Terminal

The prompts

Storing the key asks for what the CMS showed when the key was made: You don’t type the site’s slug: the SDK reads it from the key (the acme in fsk_acme_…). Once the row is stored, the SDK calls the site with it and reports three things: the site is reachable, the key is accepted, and your server’s clock is close enough to the site’s to sign requests.
Terminal
The table is checked before you’re asked for anything. With no table yet, you’re told to run the install above, or your migrations when the migration is already published. With the table there but FETCHSKI_ATTRACT_SITES_FROM not set to database, you’re told to set it.

Call the site

Nothing changes in your code. With one row in the table, that row is the default site:
app/Actions/LatestPosts.php

Rotate the key

When you have a new key for the site from its CMS, swap it in:
Terminal
It keeps the site’s row and URL, asks for the new key, its signing secret and its signing mode (the current one is offered first), and refuses a key that belongs to another site. The old key and secret are gone from the table the moment the new ones are stored, then the new key is checked on the site. With more than one site in the table, name the one to rotate:
Terminal
The old key keeps working on the site until you switch it off in its CMS. Long-running processes keep the client they already made. If your app runs queue workers, restart them so they pick up the new key:
Terminal
If it runs on Octane, reload it:
Terminal

In a script or a deploy

Give every value as an option and nothing is asked:
Terminal
Every command and option is on Artisan commands.
Options are saved in your shell history and can show up in a process list. Read them from your secret store in a script, and use the prompts when you type it yourself.

Forget the key

To take the key out of your app, delete its row:
Terminal
You’re asked Delete the API key for acme from the attract_api_keys table? first. If the table holds keys for more than one site, name the site:
Terminal
Deleting the row doesn’t switch the key off: it keeps working on the site until you revoke it there too, in the CMS under API Keys.

The table and its model

The migration creates this table: Fetchski\AttractCmsLaravel\Models\AttractApiKey is an ordinary Eloquent model over it, so you can query it like any other, for an admin page that shows which site the app is connected to, say:
$key->token and $key->signing_secret read back decrypted, so never print or log them. toArray() and toJson() leave them out.

Add your own columns

Extend the model, add the columns in a migration of your own, and point the config at your class.
1

Make the migration

Terminal
database/migrations/xxxx_xx_xx_xxxxxx_add_notes_to_attract_api_keys_table.php
2

Run it

Terminal
3

Extend the model

Keep the parent’s fillable columns and add yours:
app/Models/AttractApiKey.php
4

Point the config at it

In config/fetchski-attract.php (published by the install):
config/fetchski-attract.php
The SDK now uses your model, for storing and forgetting keys too. A class that doesn’t extend the package’s model is refused, with a message saying so.

Test with it

Attract::fake() needs no table, so tests that fake the site stay as they are (Testing). To test code that reads the stored key, store one in the test database:
tests/Feature/AttractKeyTest.php
The stored key can only be read with the APP_KEY it was encrypted with. If your app’s APP_KEY is ever rotated, Laravel reads the stored key with the old one as long as it’s listed in APP_PREVIOUS_KEYS. To move the key under the new APP_KEY for good, make a new API key in the CMS and swap it in:
Terminal