Authorizing a gated resource without login
This is a passwordless login package, but the same single-use, hashed-at-rest token can authorize access to one specific resource — a one-time file download, a gated view — without logging anyone in and without a serialized payload. Mint a token, put the raw token on your own route, and consume it there.
Mint the token
use EmailMagicLink\Contracts\TokenStore;
use Illuminate\Support\Facades\URL;
// Mint a single-use token for the user — nothing is sent.
$issued = app(TokenStore::class)->issue($user, config('auth.defaults.guard'), 'link');
// Build a link to YOUR OWN route with the raw token. It is a 256-bit unguessable
// secret; sign the route as well for URL-level expiry and tamper-resistance.
$url = URL::temporarySignedRoute('invoices.download', $issued->record->expires_at, [
'token' => $issued->plaintext,
]);
// deliver $url however you like — email, SMS, chat …
Consume it on your own route
// On your route, authorize by consuming the token — without logging anyone in.
Route::get('/invoices/download', function (Request $request) {
$token = (string) $request->query('token');
$result = app(TokenStore::class)->claimLink($token);
abort_unless($result->successful, 403);
// The claim is atomic and single-use; serve the resource for the token's user.
return Storage::download(invoicePathFor($result->token->user_id));
})->middleware('signed')->name('invoices.download');
Why this works
Because you call claimLink() yourself instead of the bundled consume flow, no session is
created — a successful claim is simply your authorization to serve the resource. The token is
consumed atomically, stored only as a hash, and expires on its own; a second visit fails
exactly like any spent link.
Variations
- Allow a bounded number of downloads by minting with
issue($user, $guard, 'link', maxUses: 3)— the same atomic decrement described under multi-use links. - Prefer a short code to a URL token with
EmailMagicLink::issueCode($user), which returns a rawcodeyou can pass around instead. Claim it withclaimCode($user, $code, $guard). - Add a shared secret by minting with a
passphraseand verifying it before you serve — see passphrase-gated links.
The TokenStore signatures and what each guarantees are in the
contract reference.