Skip to content

Instantly share code, notes, and snippets.

@nginx-gists
Last active February 26, 2024 13:10
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save nginx-gists/340731282df4b32f2266b9ddf4bd5de5 to your computer and use it in GitHub Desktop.
Save nginx-gists/340731282df4b32f2266b9ddf4bd5de5 to your computer and use it in GitHub Desktop.
Validating OAuth 2.0 Access Tokens with NGINX and NGINX Plus
server {
listen 80;
location / {
auth_request /_oauth2_token_introspection;
proxy_pass http://my_backend;
}
location = /_oauth2_token_introspection {
internal;
proxy_method POST;
proxy_set_header Authorization "bearer SecretForOAuthServer";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_body "token=$http_apikey&token_hint=access_token";
proxy_pass https://idp.example.com/oauth/token;
}
}
# vim: syntax=nginx
keyval_zone zone=access_tokens:4m timeout=100s sync;
keyval $http_apikey $token_data zone=access_tokens;
js_import oauth2.js; # Location of JavaScript code
server {
listen 80;
location / {
auth_request /_oauth2_token_introspection;
auth_request_set $username $sent_http_token_username;
proxy_set_header X-Username $username;
proxy_pass http://my_backend;
}
location = /_oauth2_token_introspection {
internal;
js_content oauth2.introspectAccessToken;
}
location /_oauth2_send_request {
internal;
proxy_method POST;
proxy_set_header Authorization "Bearer SecretForOAuthServer";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_body "token=$http_apikey&token_hint=access_token";
proxy_pass https://idp.example.com/oauth/token/introspect;
}
}
# vim: syntax=nginx
proxy_cache_path /var/cache/nginx/oauth keys_zone=token_responses:1m max_size=2m;
js_import oauth2.js; # Location of JavaScript code
server {
listen 80;
location / {
auth_request /_oauth2_token_introspection;
proxy_pass http://my_backend;
}
location = /_oauth2_token_introspection {
internal;
js_content oauth2.introspectAccessToken;
}
location /_oauth2_send_request {
internal;
proxy_method POST;
proxy_set_header Authorization "Bearer SecretForOAuthServer";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_body "token=$http_apikey&token_hint=access_token";
proxy_pass https://idp.example.com/oauth/token/introspect;
proxy_cache token_responses; # Enable caching
proxy_cache_key $http_apikey; # Cache for each access token
proxy_cache_lock on; # Duplicate tokens must wait
proxy_cache_valid 200 10s; # How long to use each response
proxy_ignore_headers Cache-Control Expires Set-Cookie;
}
}
# vim: syntax=nginx
keyval_zone zone=access_tokens:4m timeout=10s sync;
keyval $http_apikey $token_data zone=access_tokens;
js_import oauth2.js; # Location of JavaScript code
server {
listen 80;
location / {
auth_request /_oauth2_token_introspection;
proxy_pass http://my_backend;
}
location = /_oauth2_token_introspection {
internal;
js_content oauth2.introspectAccessToken;
}
location /_oauth2_send_request {
internal;
proxy_method POST;
proxy_set_header Authorization "Bearer SecretForOAuthServer";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_body "token=$http_apikey&token_hint=access_token";
proxy_pass https://idp.example.com/oauth/token/introspect;
}
}
# vim: syntax=nginx
js_import oauth2.js; # Location of JavaScript code
server {
listen 80;
location / {
auth_request /_oauth2_token_introspection;
proxy_pass http://my_backend;
}
location = /_oauth2_token_introspection {
internal;
js_content oauth2.introspectAccessToken;
}
location /_oauth2_send_request {
internal;
proxy_method POST;
proxy_set_header Authorization "Bearer SecretForOAuthServer";
proxy_set_header Content-Type "application/x-www-form-urlencoded";
proxy_set_body "token=$http_apikey&token_hint=access_token";
proxy_pass https://idp.example.com/oauth/token/introspect;
}
}
# vim: syntax=nginx
function introspectAccessToken(r) {
r.subrequest("/_oauth2_send_request",
function(reply) {
if (reply.status == 200) {
var response = JSON.parse(reply.responseText);
if (response.active == true) {
r.return(204); // Token is valid, return success code
} else {
r.return(403); // Token is invalid, return forbidden code
}
} else {
r.return(401); // Unexpected response, return 'auth required'
}
}
);
}
export default { introspectAccessToken }
function introspectAccessToken(r) {
if (r.variables.token_data) {
tokenResult(r); // Existing response in key-value store so do validation
} else {
r.subrequest("/_oauth2_send_request",
function(reply) {
if (reply.status == 200) {
r.variables.token_data = reply.responseText; // Create entry
tokenResult(r); // Do validation of response
} else {
r.return(401); // Unexpected response, return auth-required
}
}
);
}
}
function tokenResult(r) {
var response = JSON.parse(r.variables.token_data);
if (response.active) {
// Convert all members of the response into response headers
for (var p in response) {
if (!response.hasOwnProperty(p)) continue;
r.log("OAuth2 Token-" + p + ": " + response[p]);
r.headersOut['Token-' + p] = response[p];
}
r.status = 204;
r.sendHeader();
r.finish();
} else {
r.return(401);
}
}
export default { introspectAccessToken }
function introspectAccessToken(r) {
if (r.variables.token_data) {
tokenResult(r); // Existing response in key-value store so do validation
} else {
r.subrequest("/_oauth2_send_request",
function(reply) {
if (reply.status == 200) {
r.variables.token_data = reply.responseText; // Create entry
tokenResult(r); // Do validation of response
} else {
r.return(401); // Unexpected response, return auth-required
}
}
);
}
}
function tokenResult(r) {
var response = JSON.parse(r.variables.token_data);
if (response.active) {
r.return(204);
} else {
r.return(401);
}
}
export default { introspectAccessToken }
@nginx-gists
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment