skip to content

With Laravel Socialite, which provider tokens does the callback return, and how do you keep calling the provider's API after expiry?

level: middleimportance: nice to knowfreq 22%

answer

  1. token, refreshToken, expiresIn
  2. approvedScopes on the same object
  3. refreshToken() returns a Token
  4. Google keeps the old refresh token
  5. encrypted cast, not a plain column

basics

~10 s

The Socialite user carries token, refreshToken, expiresIn and approvedScopes. Store them encrypted if you need provider API access, and when the access token expires call Socialite::driver(...)->refreshToken($refresh), which returns a Laravel\Socialite\Two\Token with fresh values.

solid answer

~40 s

After `user()`, the `Laravel\Socialite\Two\User` exposes `token` (the access token), `refreshToken` (may be null if the provider issued none), `expiresIn` (seconds, may be null) and `approvedScopes`. Sign-in alone needs none of them. If the developer-community site later calls the GitHub or Google API for the member, for example to list repositories, store them with an `encrypted` cast and record when the token expires. When it has expired, call `Socialite::driver('google')->refreshToken($stored)`. It posts a `refresh_token` grant with your client credentials and returns a `Laravel\Socialite\Two\Token` with `token`, `refreshToken`, `expiresIn` and `approvedScopes`. The Google driver keeps your old refresh token when the response carries no new one; the generic driver does not, so save whatever comes back. Whether a provider issues refresh tokens at all, and on what terms, is up to that provider.

go deeper

for a junior

Recall the token, refreshToken, expiresIn and approvedScopes properties on the Socialite user, and that sign-in itself does not require storing them.

for a middle

Explain how refreshToken() works, what the returned Token contains, and how to store tokens with encryption and an absolute expiry.

for a senior

Plan for driver differences and failure: missing refresh tokens, revoked consent, and background jobs that refresh just before use.

for a principal

Decide which features justify holding provider credentials at all, and the retention and revocation policy for them.

## What the callback gives you `Socialite::driver('github')->user()` returns a `Laravel\Socialite\Two\User`. Besides the profile getters, it carries the token response from the code exchange: | Property | Filled from | Notes | |---|---|---| | `token` | `access_token` | the bearer token for the provider's API | | `refreshToken` | `refresh_token` | null when the provider issued none | | `expiresIn` | `expires_in` | seconds; null when the provider sends none | | `approvedScopes` | `scope` | split on the driver's separator | **Signing in needs none of these.** The member's identity is `getId()`; once they are mapped and signed in, the provider token is only useful if your app calls the provider's API on their behalf. ## Deciding whether to keep them On a developer-community site, a "show my public repositories" feature needs the GitHub token later; a plain "Sign in with Google" does not need Google's token at all. Keep tokens only for features that use them: - A token you never store cannot leak from your database. - A stored token is a live credential for the member's provider account, limited by `approvedScopes`. ## Storing them safely ```php protected function casts(): array { return [ 'token' => 'encrypted', 'refresh_token' => 'encrypted', 'token_expires_at' => 'datetime', ]; } ``` - Put them on the provider-link row (`social_accounts`), not on `users`, so each provider has its own set. - Use the `encrypted` cast so a database dump does not expose working tokens. - Convert `expiresIn` to an absolute `token_expires_at` at save time; a relative number of seconds is meaningless later. - Hide them from any serialized output. ## Refreshing `refreshToken($refreshToken)` exists on Socialite's OAuth 2 providers: 1. It posts `grant_type=refresh_token`, the refresh token, `client_id` and `client_secret` to the driver's token URL. 2. It returns a `Laravel\Socialite\Two\Token` object with public `token`, `refreshToken`, `expiresIn` and `approvedScopes`. 3. You overwrite the stored values and the new expiry. ```php $fresh = Socialite::driver('google')->refreshToken($account->refresh_token); $account->update([ 'token' => $fresh->token, 'refresh_token' => $fresh->refreshToken, 'token_expires_at' => now()->addSeconds($fresh->expiresIn), ]); ``` Driver differences matter here: - The **Google** driver overrides `refreshToken()` so that, when the response has no new refresh token, the returned `Token` keeps the one you passed in. - The **base** implementation passes whatever the response contains; the `Token` constructor has non-nullable `string` and `int` parameters, so a response that omits `refresh_token` or `expires_in` makes the call throw a `TypeError` rather than return an empty token. - Whether a refresh token is issued at all, and for how long it lasts, is the provider's policy. Some providers need an extra authorization parameter, passed with `with()`, before they issue one. ## Check scopes before you call A stored token only carries the permissions in `approvedScopes`. Before a job calls the provider's API, check that the permission it needs is in that list; if it is not, send the member through the redirect again with `scopes([...])` rather than letting the call fail with a permission error. Record the granted scopes next to the token so the check needs no extra provider call. ## Where refresh runs - In a **queued job** that syncs repositories, refresh just before the API call when `token_expires_at` is past. - Handle a failed refresh (revoked consent, expired refresh token) by marking the link as needing re-authorisation and asking the member to connect again. ## Summary - `user()` hands you `token`, `refreshToken`, `expiresIn`, `approvedScopes`. - Store them only when a feature calls the provider, encrypted, with an absolute expiry. - `refreshToken()` returns a `Token`; save every field it gives back.

  • Why store an absolute token_expires_at instead of the expiresIn value?
    `expiresIn` is a number of seconds counted from the moment of the token response. Stored as-is, a job running days later cannot tell whether the token is still valid. Converting it with `now()->addSeconds($expiresIn)` at save time gives a timestamp any later code can compare with the current time.
  • What happens when the stored refresh token has been revoked?
    The provider's token endpoint answers with an error, and the HTTP client Socialite uses throws, so `refreshToken()` does not return a `Token`. Catch that failure, clear or flag the stored tokens, and prompt the member to connect the provider again through the normal redirect flow.

saying these in an interview costs you the question

  • You must store the access token to keep a member signed in
  • Every provider returns a refresh token on every login
  • refreshToken() updates the stored tokens in your database automatically
  • Provider tokens are safe in plain columns because they expire
  • expiresIn is an absolute Unix timestamp