Google - v1.0.0
Connect
GoogleAuthController::connect() and OAuthManager::authorizationUrl() build the redirect that sends a user to Google's consent screen.
The controller
public function connect( Request $request ): RedirectResponse
{
$user = $request->user();
if ( null === $user ) {
abort( 401 );
}
$url = $this->oauth->authorizationUrl( $user->getAuthIdentifier() );
return redirect()->away( $url );
}
Route: GET /google/auth/connect → google.auth.connect.
Building the URL
OAuthManager::authorizationUrl() delegates to buildAuthorizationUrl():
$params = [
'client_id' => $this->config->getClientId(),
'redirect_uri' => $this->config->getRedirectUri(),
'response_type' => 'code',
'scope' => implode( ' ', $scopes ),
'access_type' => 'offline',
'prompt' => 'consent',
'include_granted_scopes' => 'true',
'state' => $state,
'code_challenge' => $challenge,
'code_challenge_method' => 'S256',
];
Parameters explained
response_type=code— Authorization Code grant. We exchange the code for tokens server-side.scope— Space-separated list fromScopeRegistry::all(). Baseline isopenid userinfo.email userinfo.profile; service packages add viaap.google.scopes. See Scopes.access_type=offline— Required to receive arefresh_token. Without this Google issues an access-token-only response and you'd have no way to refresh.prompt=consent— Forces the consent screen every time. This guarantees therefresh_tokenis included in the response — Google only re-issues it when the user actually clicks "Allow", not when they silently re-consent to previously approved scopes. Withoutprompt=consent, repeat connects from the same account get access tokens with no refresh token attached.include_granted_scopes=true— Google merges any newly granted scopes with existing ones. This matters for incremental consent and is harmless on a first connect.state— 40-char random string, stored in the session. On callback, compared withhash_equals()against the returned value. Defeats CSRF.code_challenge+code_challenge_method=S256— PKCE. The verifier (a URL-safe base64 of 64 random bytes) is stored in the session; the challenge is the URL-safe base64 ofSHA-256(verifier). Google returns the code to the redirect URI and requires the verifier on the token exchange call — so an attacker who intercepts the redirect can't exchange the code without also having the verifier.
Session state
Three keys are written to the session:
| Key | Purpose |
|---|---|
google.oauth.state |
Verified against the callback's state parameter. |
google.oauth.verifier |
Sent as code_verifier in the token exchange. |
google.oauth.user_id |
The user we're building the connection for. Pulled out on callback so handleCallback() can attach the new tokens to the right row. |
All three are pull()ed (read + deleted) on callback, so a subsequent replay of the same callback URL fails cleanly.
Not-configured errors
If the credential driver reports isConfigured() === false, buildAuthorizationUrl() throws:
throw new OAuthException( __( 'Google OAuth credentials are not configured.' ) );
The default controller does not catch this — it will bubble up as a 500. If you're using the built-in routes, gate the "Connect Google" button behind a check first:
@if( Google::config()->isConfigured() )
<a href="{{ route('google.auth.connect') }}">Connect Google</a>
@else
<p>Google integration is not configured yet.</p>
@endif
Or wrap the controller with your own middleware that surfaces a friendlier error.
Calling the manager directly
If your app has its own controller / URL structure and you set google.routes.enabled = false, resolve the manager and build the URL yourself:
use ArtisanPackUI\Google\Facades\Google;
$url = Google::oauth()->authorizationUrl( $user->id );
return redirect()->away( $url );
You can also override the scopes for a specific flow:
$url = Google::oauth()->authorizationUrl(
$user->id,
override: [ 'openid', 'https://www.googleapis.com/auth/analytics.readonly' ],
);
This is unusual — service packages normally register their scopes through the ap.google.scopes filter hook and rely on the union — but it's there when you need it.