Laravel AT Protocol Client (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpClient\Data;
4
5use Carbon\Carbon;
6use SocialDept\AtpClient\Enums\AuthType;
7
8class AccessToken
9{
10 public function __construct(
11 public readonly string $accessJwt,
12 public readonly string $refreshJwt,
13 public readonly string $did,
14 public readonly \DateTimeInterface $expiresAt,
15 public readonly ?string $handle = null,
16 public readonly ?string $issuer = null,
17 public readonly array $scope = [],
18 public readonly AuthType $authType = AuthType::OAuth,
19 ) {}
20
21 /**
22 * Create from API response.
23 *
24 * Handles both legacy createSession format (accessJwt, refreshJwt, did)
25 * and OAuth token format (access_token, refresh_token, sub).
26 */
27 public static function fromResponse(array $data, ?string $handle = null, ?string $issuer = null): self
28 {
29 // OAuth token endpoint format
30 if (isset($data['access_token'])) {
31 return new self(
32 accessJwt: $data['access_token'],
33 refreshJwt: $data['refresh_token'] ?? '',
34 did: $data['sub'] ?? '',
35 expiresAt: now()->addSeconds($data['expires_in'] ?? 300),
36 handle: $handle,
37 issuer: $issuer,
38 scope: isset($data['scope']) ? explode(' ', $data['scope']) : [],
39 authType: AuthType::OAuth,
40 );
41 }
42
43 // Legacy createSession format (app passwords have full access)
44 // Parse expiry from JWT since createSession doesn't return expiresIn
45 $expiresAt = self::parseJwtExpiry($data['accessJwt']) ?? now()->addHour();
46
47 return new self(
48 accessJwt: $data['accessJwt'],
49 refreshJwt: $data['refreshJwt'],
50 did: $data['did'],
51 expiresAt: $expiresAt,
52 handle: $data['handle'] ?? $handle,
53 issuer: $issuer,
54 scope: ['atproto', 'transition:generic', 'transition:email'],
55 authType: AuthType::Legacy,
56 );
57 }
58
59 /**
60 * Parse the expiry timestamp from a JWT's payload.
61 */
62 protected static function parseJwtExpiry(string $jwt): ?\DateTimeInterface
63 {
64 $parts = explode('.', $jwt);
65
66 if (count($parts) !== 3) {
67 return null;
68 }
69
70 $payload = json_decode(base64_decode(strtr($parts[1], '-_', '+/')), true);
71
72 if (! isset($payload['exp'])) {
73 return null;
74 }
75
76 return Carbon::createFromTimestamp($payload['exp']);
77 }
78}