On this page:
- spider.google: Sign-in with Google (OAuth):
loginsends the visitor to Google,callbackchecks the answer and returns their profile.
spider.google
Google sign-in (spider.google): the OAuth authorization-code flow. login sends the visitor to Google, callback receives them back, checks that the login was started by this browser, and returns their profile. No middleware and no session: the app decides what to do with the profile (usually spider.session.start).
fn googleLogin(c: *spider.Ctx) !spider.Response {
return spider.google.login(c, google_config);
}
fn googleCallback(c: *spider.Ctx) !spider.Response {
const profile = try spider.google.callback(c, google_config);
return c.redirectWith("/", try spider.session.start(c, .{
.id = profile.id, .email = profile.email, .name = profile.name,
}));
}
spider.google.GoogleConfig
pub const GoogleConfig = struct
The OAuth client created in the Google Cloud console. Its id, secret and redirect address have no default and nothing is read from the environment: the app fills them. None of the strings is copied.
Fields
client_id: []const u8: The OAuth client's id.client_secret: []const u8: The OAuth client's secret, sent when the code is exchanged.redirect_uri: []const u8: The full URL of the app's callback route, as registered for the client. The login and the callback must be given the same value. When it starts withhttp://,loginleavesSecureoff its state cookie.auth_endpoint: []const u8 = "https://accounts.google.com/o/oauth2/v2/auth": Google's consent page. Like the two below, it is here so that a test can point the flow at a server of its own; an app leaves them alone.token_endpoint: []const u8 = "https://oauth2.googleapis.com/token": Where the code is exchanged for a token.userinfo_endpoint: []const u8 = "https://www.googleapis.com/oauth2/v2/userinfo": Where the profile is read with the token.
spider.google.state_cookie
pub const state_cookie = "google_oauth_state"
The cookie login sets and callback checks.
spider.google.Error
pub const Error = error
What callback and fetchProfile fail with. When the handler returns one, the server answers 400 for the first two, 401 for a refused code and 502 when Google does not give the profile; an app's onError may show a page instead.
Errors
OAuthStateMismatch: Thestateof the callback is not the one this browser was given bylogin(or there is none): the login was not started here.OAuthCodeMissing: The callback came without acode(the visitor refused, usually).OAuthCodeRejected: Google did not accept the code: used before, expired, or made for another client or redirect address. In the code: the token endpoint did not answer 200 with a JSON body that has anaccess_token.OAuthProfileFailed: Google accepted the code and then did not return the profile: the userinfo endpoint did not answer 200 with a JSON body that has anid.
spider.google.login
pub fn login(c: *Ctx, config: GoogleConfig) !Response
For the route that starts the sign-in: redirects (302) the visitor to Google's consent page with a random state, also kept in a cookie of this browser (HttpOnly, SameSite=Lax, 10 minutes; Secure unless redirect_uri is plain http, as on a developer's machine). callback compares the two.
spider.google.callback
pub fn callback(c: *Ctx, config: GoogleConfig) !GoogleProfile
For the callback route: checks the state against the cookie login set, then exchanges the code and returns the profile (fetchProfile). Nothing is asked of Google before the state matches. Fails with one of Error, or with the HTTP client's error when Google cannot be reached. The state is checked before the code, so a visitor who refused consent gets error.OAuthCodeMissing. The cookie stays (this function returns a profile, not a response) and expires by itself 10 minutes after login set it; clearState removes it with the response that follows.
spider.google.clearState
pub fn clearState(c: *Ctx, config: GoogleConfig) ![2][]const u8
A cookie for ResponseOptions.cookies that removes the state cookie login set, for the response that follows a callback:
const profile = try spider.google.callback(c, config);
const cookies = try c.arena.alloc([2][]const u8, 1);
cookies[0] = try spider.google.clearState(c, config);
return c.redirectWith("/", .{ .status = .found, .cookies = cookies });
spider.google.GoogleProfile
pub const GoogleProfile = struct
The user as Google's userinfo endpoint describes them. The strings live in the request arena. Only id is always there: the other three are empty when Google's answer does not have them.
Fields
id: []const u8: Google's stable id for the account.email: []const u8name: []const u8picture: []const u8: URL of the account's picture.
spider.google.authUrl
pub fn authUrl(arena: std.mem.Allocator, config: GoogleConfig) ![]u8
The Google consent page to redirect the user to, asking for the openid email profile scopes with access_type=offline. Allocated in arena; the config values are URL-encoded. It adds no state: login does, and is the one a login route should call (or authUrlWith, to send a state of your own).
spider.google.AuthUrlOptions
pub const AuthUrlOptions = struct
Options of authUrlWith.
Fields
state: ?[]const u8 = null: Sent to Google and given back to the redirect address as?state=. Put something only this visitor's browser holds (a random value also kept in a cookie) and compare the two in the callback: without it, someone else can finish a login in the visitor's browser.
spider.google.authUrlWith
pub fn authUrlWith(arena: std.mem.Allocator, config: GoogleConfig, opts: AuthUrlOptions) ![]u8
authUrl with a state, URL-encoded like the rest:
const url = try spider.google.authUrlWith(c.arena, config, .{ .state = nonce });
spider.google.fetchProfile
pub fn fetchProfile(c: *Ctx, code: []const u8, config: GoogleConfig) !GoogleProfile
Exchanges code (the callback's ?code=) for an access token and fetches the user's profile with it: two HTTP requests to Google. The profile is allocated in c.arena. It does not look at the state: callback does, and is the one a callback route should call.
Fails with error.OAuthCodeRejected when Google does not accept the code, error.OAuthProfileFailed when it then does not return the profile, or the HTTP client's error when it cannot be reached.