declare local var.isAuthenticated BOOL;
if (:auth) {
log "Request contains a session cookie. Validate it.";
declare local var.decodedCookie STRING;
declare local var.suppliedSig STRING;
declare local var.expectedSig STRING;
set var.decodedCookie = urldecode(:auth);
set var.suppliedSig = subfield(var.decodedCookie, "sig", "&");
set var.expectedSig = digest.base64(
digest.hmac_sha1(
table.lookup(oauth_settings, "session_secret"),
subfield(var.decodedCookie, "email", "&") subfield(var.decodedCookie, "expires", "&")
)
);
if (var.suppliedSig == var.expectedSig) {
declare local var.expires TIME;
set var.expires = std.integer2time(
std.atoi(subfield(var.decodedCookie, "expires", "&"))
);
if (!time.is_after(now, var.expires)) {
set var.isAuthenticated = true;
set = subfield(var.decodedCookie, "email", "&");
}
} else {
log "Signature of session cookie is invalid. Expected " var.expectedSig ", got " var.suppliedSig;
error 200 "oauth_error: Invalid session cookie";
}
}
if (req.url.path == table.lookup(oauth_settings, "redirect_uri")) {
if (!subfield(req.url.qs, "id_token","&")) {
log "Received post-auth callback but no ID token present, probably because it's a hash-fragment";
error 200 "oauth_capture_implicit_code";
} else {
log "Post-auth callback with ID token (" req.url "). Decoding the JWT";
declare local var.oauthState STRING;
declare local var.oauthStateTime STRING;
declare local var.oauthStateSigExpected STRING;
declare local var.oauthStateSigSupplied STRING;
declare local var.jwtStringToSign STRING;
declare local var.jwtHeader STRING;
declare local var.jwtPayload STRING;
declare local var.jwtSuppliedSig STRING;
declare local var.jwtKeyID STRING;
declare local var.google_email STRING;
declare local var.google_email_verified STRING;
declare local var.google_issuer STRING;
declare local var.google_audience STRING;
declare local var.google_issued_at STRING;
declare local var.google_expire_time STRING;
if (subfield(req.url.qs, "id_token","&") !~ "^(([^\.]*)\.([^\.]*))\.([^&]*)$") {
error 200 "oauth_error: Invalid JWT format";
}
set var.jwtStringToSign = re.group.1;
set var.jwtHeader = digest.base64_decode(re.group.2);
set var.jwtPayload = digest.base64_decode(re.group.3);
set var.jwtSuppliedSig = re.group.4;
if (var.jwtHeader !~ {""alg": ?"RS256""}) {
error 200 "oauth_error: Unsupported JWT signing algorithm";
}
if (var.jwtHeader !~ {""kid": ?"([^"]*)""}) {
error 200 "oauth_error: Missing Google key ID";
}
set var.jwtKeyID = re.group.1;
if ( != var.jwtKeyID) {
log "Received post-auth callback with an ID token but don't have the cert needed to verify it. Rewrite the request to fetch the required key.";
set req.backend = F_origin_1;
set = req.url;
set req.url = table.lookup(oauth_settings, "cert_backend_path") "?kid=" var.jwtKeyID;
return (lookup);
} else {
log "Required Google public key (" ") is available: verify the token";
if (!digest.rsa_verify(sha256, urldecode(), var.jwtStringToSign, var.jwtSuppliedSig)) {
error 200 "oauth_error: Unable to verify signature of Google JWT";
}
set var.google_email = if(var.jwtPayload ~ {".*"email":\s*"([^"]*)"}, re.group.1, "");
set var.google_email_verified = if(var.jwtPayload ~ {".*"email_verified":\s*([^,}]*)"}, re.group.1, "");
set var.google_issuer = if(var.jwtPayload ~ {".*"iss":\s*"([^"]*)"}, re.group.1, "");
set var.google_audience = if(var.jwtPayload ~ {".*"aud":\s*"([^"]*)"}, re.group.1, "");
set var.google_expire_time = if(var.jwtPayload ~ {".*"exp":\s*([^,}]*)"}, re.group.1, "");
set var.google_issued_at = if(var.jwtPayload ~ {".*"iat":\s*"([^"]*)"}, re.group.1, "");
if (var.google_expire_time !~ "^\d{10,11}$") {
error 200 "oauth_error: Unable to parse valid Unix expiration timestamp from Google token.";
}
if (time.is_after(now, std.integer2time(std.atoi(var.google_expire_time)))) {
error 200 "oauth_error: Google session expiration time has already elapsed.";
}
set var.oauthState = subfield(req.url.qs, "state", "&");
if (var.oauthState !~ "^(\d+)-(\w+)$") {
log "Querystring is " req.url.qs;
log "State param is " var.oauthState;
error 200 "oauth_error: Invalid or missing Fastly state token.";
}
set var.oauthStateTime = re.group.1;
set var.oauthStateSigSupplied = re.group.2;
set var.oauthStateSigExpected = digest.hash_sha256(urldecode(:oauth_orig_url) "-" var.oauthStateTime "-" table.lookup(oauth_settings, "state_secret"));
if (var.oauthStateSigExpected != var.oauthStateSigSupplied) {
error 200 "oauth_error: Unable to verify Fastly state signature.";
}
if (time.is_after(now, std.integer2time(std.atoi(var.oauthStateTime)))) {
log "Fastly state token has expired; now (" now.sec ") is after expiry time (" var.oauthStateTime "). Restart the auth flow.";
set req.url = :oauth_orig_url;
error 200 "oauth_authorize";
}
log "OAuth token is good. Save as a local session state.";
set = var.google_email;
error 200 "oauth_save_local_session";
}
}
}
if (!var.isAuthenticated && req.url !~ "\.(ico|gif|jpg|png|js|css)$") {
log "Unauthenticated request received. Begin authentication process.";
error 200 "oauth_authorize";
}
log "We've got a session cookie, so everything looks good for fetching the requested page from cache or origin";