Skip to content

Find the valid tokens of a model, without knowing a token - #9

Merged
khanzadimahdi merged 1 commit into
masterfrom
feat/valid-tokens-of-a-model
Aug 14, 2026
Merged

Find the valid tokens of a model, without knowing a token#9
khanzadimahdi merged 1 commit into
masterfrom
feat/valid-tokens-of-a-model

Conversation

@khanzadimahdi

Copy link
Copy Markdown
Member

Closes #6.

Every lookup of the package needed the token itself, so a "forgot password" flow that wants to know whether the user
still has a usable pin had to walk temporaryTokens and call isValid() on each one, as @paulo-hortelan described in
the issue.

What a model can answer now

if (!$user->hasValidTemporaryToken('reset-password')) {
    $user->temporaryTokenBuilder()
        ->setType('reset-password')
        ->setUsageLimit(1)
        ->setExpireDate(Carbon::now()->addMinutes(5))
        ->build(6);
}

$tokenObjects = $user->validTemporaryTokens('reset-password'); // eloquent collection, latest one first

The type is optional: without one, every token of the model is looked at.

The builder answers the same questions, and without a related item it looks at the tokens of every model:

TokenBuilder::setRelatedItem($user)->setType('reset-password')->findValidTokens();
TokenBuilder::setType('reset-password')->hasAnyValidToken();

They are named for what they ask — is there any valid token — because they ignore setUniqueId().
findValidToken() and isValid() stay the way to validate a token you have, and a note in the readme says so.

Changes

  • HasTemporaryTokens: validTemporaryTokens(?string $type = null) and hasValidTemporaryToken(?string $type = null).
  • Builder (through the Validation concern, so the facade has them too): findValidTokens() and hasAnyValidToken().
  • TokensRepository: findValidTokens(?string $type, ?Model $tokenable) and hasAnyValidToken(?string $type, ?Model $tokenable).
    Its private query was built around the token it looked for; it takes the model and the type now, and the token is a
    filter that the two single-token lookups add, so the same scoping (and the same "has to use the trait" check) serves
    every lookup.
  • TokenBuilder facade: the two new methods are annotated.
  • The readme has a The valid tokens of a model section, reference entries for both new builder methods and a new
    Model reference section for the trait; the changelog has an entry.

No existing signature or behaviour changed, so this is additive.

Tests

14 tests were added, in the repository, the validation concern, a new HasTemporaryTokensTest and the readme flow of a
reset pin end to end — including that an expired one and a used up one do not count, that the tokens of another user or
of another type do not count, and that the unique id is not part of the lookup.

make ci (coding style, PHPStan level 7, test suite) is green: 69 tests, 200 assertions, and coverage of src/ is at
100%.

🤖 Generated with Claude Code

A "forgot password" flow has to know whether the user still has a pin
that is valid before it sends a new one, and until now that meant
walking `temporaryTokens` and calling `isValid()` on every one of them:
every lookup of the package needed the token itself.

`HasTemporaryTokens` answers it now, for every token of the model or for
one type of them:

    $user->hasValidTemporaryToken('reset-password');
    $user->validTemporaryTokens('reset-password');

The builder answers the same questions with `hasAnyValidToken()` and
`findValidTokens()`, and without a related item it looks at the tokens
of every model. Both are named for what they ask -- is there *any* valid
token -- because they ignore the unique id: `findValidToken()` and
`isValid()` stay the way to validate a token you have.

The query of the repository was built around the token it looked for.
It takes the model and the type now, and the token is a filter the two
lookups of a single token add, so that the same scoping serves both.

Closes #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@khanzadimahdi
khanzadimahdi merged commit 4e73e7c into master Aug 14, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Checking if related User has a valid token

1 participant