Skip to content

Instantly share code, notes, and snippets.

@andrewwiik
Created September 20, 2020 23:48
Show Gist options
  • Select an option

  • Save andrewwiik/3dcb9c028b15bf359ae1053b8e8f94b9 to your computer and use it in GitHub Desktop.

Select an option

Save andrewwiik/3dcb9c028b15bf359ae1053b8e8f94b9 to your computer and use it in GitHub Desktop.
Google Auth VCL
import blob;
import http;
import urlplus;
import kvstore;
import json;
import jwt;
import str;
import headerplus;
import cookieplus;
sub vcl_init {
new gauth_config = kvstore.init();
gauth_config.set("client_id", "174207094618-5a1knqdlr1fqr1krt4akmj8240vj71o2.apps.googleusercontent.com");
gauth_config.set("client_secret", "Xh7qJvhPCP2AlHYeZHO4uIUp");
gauth_config.set("callback_path", "/api/auth/google/callback");
gauth_config.set("auth_cookie", "auth_cookie");
gauth_config.set("signing_secret", "test123");
gauth_config.set("scope", "email profile");
gauth_config.set("allowed_domain", "varnish-software.com");
new gauth_cookie_jwt_reader = jwt.reader();
new gauth_cookie_jwt_writer = jwt.writer();
}
sub gauth_handle_start {
# Get URL to Redirect to after successful auth
set req.http.X-InternalGAuth-Referrer-URL = "https://" + req.http.Host + urlplus.url_get();
# Get CallbackURL to pass to Google Auth
set req.http.X-InternalGAuth-Callback-URL = "https://" + req.http.Host + gauth_config.get("callback_path");
# Setup Redirect URL
urlplus.parse("/o/oauth2/v2/auth");
urlplus.query_add("response_type", "code");
urlplus.query_add("prompt", "select_account");
urlplus.query_add("access_type", "offline");
urlplus.query_add("redirect_uri", blob.transcode(encoded=req.http.X-InternalGAuth-Callback-URL, encoding=URL));
urlplus.query_add("scope", blob.transcode(encoded=gauth_config.get("scope"), encoding=URL));
urlplus.query_add("client_id", gauth_config.get("client_id"));
urlplus.query_add("state", blob.transcode(encoded=req.http.X-InternalGAuth-Referrer-URL, encoding=URL));
set req.http.X-InternalGAuth-Redirect = "https://accounts.google.com" + urlplus.as_string();
urlplus.parse(req.url);
return (synth(667, req.http.X-InternalGAuth-Redirect));
}
sub gauth_get_access_token {
set req.http.X-InternalGAuth-GAuth-Token = "error";
# Initialize Post Request
http.init(1);
http.req_set_method(1, "POST");
http.req_set_url(1, "https://oauth2.googleapis.com/token");
http.req_set_header(1, "Host", "oauth2.googleapis.com");
http.req_set_header(1, "Content-Type", "application/x-www-form-urlencoded");
# Build Post Body
set req.http.X-InternalGAuth-Post-Body = "code=" + req.http.X-InternalGAuth-Callback-Code;
set req.http.X-InternalGAuth-Post-Body = req.http.X-InternalGAuth-Post-Body + "&client_id=" + gauth_config.get("client_id") + "&client_secret=" + gauth_config.get("client_secret");
set req.http.X-InternalGAuth-Full-URL = "https://" + req.http.Host + urlplus.url_get();
set req.http.X-InternalGAuth-Post-Body = req.http.X-InternalGAuth-Post-Body + "&redirect_uri=" + blob.transcode(encoded=req.http.X-InternalGAuth-Full-URL, encoding=URL);
set req.http.X-InternalGAuth-Post-Body = req.http.X-InternalGAuth-Post-Body + "&grant_type=authorization_code";
http.req_set_sparam(1, "POSTFIELDS", req.http.X-InternalGAuth-Post-Body);
# Send Data
http.req_send(1);
http.resp_wait(1);
if (http.resp_get_status(1) == 200) {
# Get Authorization Token
json.parse(http.resp_get_body(1));
if (!json.is_valid() || !json.is_object()) {
set req.http.X-InternalGAuth-GAuth-Token = "error";
} else {
set req.http.X-InternalGAuth-GAuth-Token = json.get("access_token", "error");
}
} else {
set req.http.X-InternalGAuth-GAuth-Token = "error";
}
}
sub gauth_get_profile {
set req.http.X-InternalGAuth-GAuth-Profile = "error";
# Get User's Google Profile
# Setup Request
http.init(2);
http.req_set_method(2, "GET");
http.req_set_url(2, "https://www.googleapis.com/oauth2/v3/userinfo");
http.req_set_header(2, "Host", "www.googleapis.com");
http.req_set_header(2, "Authorization", "Bearer " + req.http.X-InternalGAuth-GAuth-Token);
# Send Request
http.req_send(2);
http.resp_wait(2);
# Analyse Response
if (http.resp_get_status(2) == 200) {
set req.http.X-InternalGAuth-GAuth-Profile = http.resp_get_body(2);
} else {
set req.http.X-InternalGAuth-GAuth-Profile = "error";
}
}
sub gauth_set_cookie {
gauth_cookie_jwt_writer.set_alg("HS256");
gauth_cookie_jwt_writer.set_exp(now + 24h);
gauth_cookie_jwt_writer.set_payload(req.http.X-InternalGAuth-GAuth-Profile);
set req.http.X-InternalGAuth-Set-Auth-Cookie = gauth_cookie_jwt_writer.generate(gauth_config.get("signing_secret"));
set req.http.X-InternalGAuth-Final-Redirect = blob.transcode(encoded=req.http.X-InternalGAuth-Callback-State, decoding=URL);
return (synth(667, req.http.X-InternalGAuth-Final-Redirect));
}
sub gauth_handle_callback {
# Get callback data from URL
set req.http.X-InternalGAuth-Callback-Code = urlplus.query_get("code", "error");
set req.http.X-InternalGAuth-Callback-State = urlplus.query_get("state");
# Did we get an auth code
if (req.http.X-InternalGAuth-Callback-Code == "error") {
return (synth(503, "No Auth Code Given"));
}
call gauth_get_access_token;
if (req.http.X-InternalGAuth-GAuth-Token == "error") {
return (synth(503, "Failed to get Authorization Bearer Token"));
}
call gauth_get_profile;
if (req.http.X-InternalGAuth-GAuth-Profile == "error") {
return (synth(503, "Failed to get User's Profile"));
}
call gauth_set_cookie;
}
sub gauth_handle {
if (urlplus.url_get() == gauth_config.get("callback_path")) {
# Callback URL for Google OAuth
call gauth_handle_callback;
} else {
# Start the Authentication Process
call gauth_handle_start;
}
}
sub gauth_check_allowed_user {
json.parse(gauth_cookie_jwt_reader.get_payload());
set req.http.X-InternalGAuth-Email = json.get("email", "error");
if (req.http.X-InternalGAuth-Email == "error") {
set req.http.X-InternalGAuth-Wipe-Cookie = "YES";
return (synth(503, "Not Authorized to Access this Resource"));
}
if (!str.endswith(req.http.X-InternalGAuth-Email, "@" + gauth_config.get("allowed_domain"))) {
set req.http.X-InternalGAuth-Wipe-Cookie = "YES";
return (synth(503, "Not Authorized to Access this Resource"));
}
}
sub gauth_check {
set req.http.X-InternalGAuth-Auth-Cookie = cookieplus.get(gauth_config.get("auth_cookie"), "error");
# No Auth Cookie was present, we need to authenticate
if (req.http.X-InternalGAuth-Auth-Cookie == "error") {
call gauth_handle;
}
# Setup Auth Cookie JWT Reader
gauth_cookie_jwt_reader.set_key(gauth_config.get("signing_secret"));
if (gauth_cookie_jwt_reader.parse(req.http.X-InternalGAuth-Auth-Cookie)) {
if (!gauth_cookie_jwt_reader.verify("HS256")) {
# JWT Auth Cookie was expired
call gauth_handle;
} else {
call gauth_check_allowed_user;
}
} else {
# Did not have valid JWT Auth Cookie
call gauth_handle;
}
# headerplus.init(req);
# headerplus.delete_regex("^X-InternalGAuth-");
# headerplus.write();
# They are Authenticated
}
sub vcl_synth {
if (resp.status == 667) {
if (req.http.X-InternalGAuth-Set-Auth-Cookie) {
cookieplus.setcookie_add(gauth_config.get("auth_cookie"), req.http.X-InternalGAuth-Set-Auth-Cookie, 24h, req.http.Host, "/", true, true);
cookieplus.setcookie_write();
}
set resp.http.location = resp.reason;
set resp.reason = "Moved";
set resp.status = 302;
return (deliver);
}
if (resp.status == 503) {
if (req.http.X-InternalGAuth-Wipe-Cookie == "YES") {
cookieplus.setcookie_add(gauth_config.get("auth_cookie"), "", -1h, req.http.Host, "/", true, true);
cookieplus.setcookie_write(resp.http.Set-Cookie);
}
}
}
sub vcl_recv {
call gauth_check;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment