+13
-2
README.md
+13
-2
README.md
···
531
531
$table->text('access_token'); // JWT access token
532
532
$table->text('refresh_token'); // Single-use refresh token
533
533
$table->timestamp('expires_at'); // Token expiration time
534
+
$table->json('scope')->nullable(); // Granted OAuth scopes
534
535
$table->timestamps();
535
536
});
536
537
```
···
564
565
expiresAt: $record->expires_at,
565
566
handle: $record->handle,
566
567
issuer: $record->issuer,
568
+
scope: $record->scope ?? [],
567
569
);
568
570
}
569
571
···
577
579
'access_token' => $token->accessJwt,
578
580
'refresh_token' => $token->refreshJwt,
579
581
'expires_at' => $token->expiresAt,
582
+
'scope' => $token->scope,
580
583
]
581
584
);
582
585
}
···
587
590
'access_token' => $token->accessJwt,
588
591
'refresh_token' => $token->refreshJwt,
589
592
'expires_at' => $token->expiresAt,
590
-
// Preserve handle and issuer, or update if provided
591
593
'handle' => $token->handle,
592
594
'issuer' => $token->issuer,
595
+
'scope' => $token->scope,
593
596
]);
594
597
}
595
598
···
618
621
'access_token',
619
622
'refresh_token',
620
623
'expires_at',
624
+
'scope',
621
625
];
622
626
623
627
protected $casts = [
624
628
'expires_at' => 'datetime',
629
+
'scope' => 'array',
625
630
];
626
631
627
632
protected $hidden = [
···
707
712
| `accessToken` | JWT for API authentication (short-lived) |
708
713
| `refreshToken` | Token to get new access tokens (single-use!) |
709
714
| `expiresAt` | When the access token expires |
715
+
| `scope` | Array of granted scopes (e.g., `['atproto', 'transition:generic']`) |
710
716
711
717
### Handling Token Refresh Events
712
718
···
736
742
use SocialDept\AtpClient\Facades\Atp;
737
743
738
744
Event::listen(OAuthUserAuthenticated::class, function (OAuthUserAuthenticated $event) {
739
-
// $event->token contains: did, accessJwt, refreshJwt, handle, issuer, expiresAt
745
+
// $event->token contains: did, accessJwt, refreshJwt, handle, issuer, expiresAt, scope
746
+
747
+
// Check granted scopes
748
+
if (in_array('atproto', $event->token->scope)) {
749
+
// User granted AT Protocol access
750
+
}
740
751
741
752
// Fetch the user's profile
742
753
$client = Atp::as($event->token->did);
+4
-1
src/Data/AccessToken.php
+4
-1
src/Data/AccessToken.php
···
11
11
public readonly \DateTimeInterface $expiresAt,
12
12
public readonly ?string $handle = null,
13
13
public readonly ?string $issuer = null,
14
+
public readonly array $scope = [],
14
15
) {}
15
16
16
17
/**
···
30
31
expiresAt: now()->addSeconds($data['expires_in'] ?? 300),
31
32
handle: $handle,
32
33
issuer: $issuer,
34
+
scope: isset($data['scope']) ? explode(' ', $data['scope']) : [],
33
35
);
34
36
}
35
37
36
-
// Legacy createSession format
38
+
// Legacy createSession format (app passwords have full access)
37
39
return new self(
38
40
accessJwt: $data['accessJwt'],
39
41
refreshJwt: $data['refreshJwt'],
···
41
43
expiresAt: now()->addSeconds($data['expiresIn'] ?? 300),
42
44
handle: $data['handle'] ?? $handle,
43
45
issuer: $issuer,
46
+
scope: ['atproto', 'transition:generic', 'transition:email'],
44
47
);
45
48
}
46
49
}
+1
src/Data/Credentials.php
+1
src/Data/Credentials.php
+1
src/Providers/ArrayCredentialProvider.php
+1
src/Providers/ArrayCredentialProvider.php
+10
src/Session/Session.php
+10
src/Session/Session.php
···
53
53
return $this->credentials->expiresIn();
54
54
}
55
55
56
+
public function scopes(): array
57
+
{
58
+
return $this->credentials->scope;
59
+
}
60
+
61
+
public function hasScope(string $scope): bool
62
+
{
63
+
return in_array($scope, $this->credentials->scope, true);
64
+
}
65
+
56
66
public function withCredentials(Credentials $credentials): self
57
67
{
58
68
return new self($credentials, $this->dpopKey, $this->pdsEndpoint);