In favour of


HTTP proxy for authenticating users via OAuth2

Latest on Hackage:0.9.9

This package is not currently in any snapshots. If you're interested in using it, we recommend adding it to Stackage Nightly. Doing so will make builds more reliable, and allow to host generated Haddocks.

MIT licensed by Chris Forno, Igor Pashev
Maintained by Igor Pashev

Sproxy - HTTP proxy for authenticating users via OAuth2


Why use a proxy for doing OAuth? Isn’t that up to the application?

  • sproxy is secure by default. No requests make it to the web server if they haven’t been explicitly whitelisted.
  • sproxy is independent. Any web application written in any language can use it.

Use cases

  • Existing web applications with concept of roles. For example, Mediawiki, Jenkins, Icinga Web 2. In this case you configure Sproxy to allow unrestricted access to the application for some groups defined by Sproxy. These groups are mapped to the application roles. There is a plugin for Jenkins which can be used for this. Mediawiki and Icinga Web 2 were also successfully deployed in this way, though it required changes to their source code.

  • New web applications designed to work specifically behind Sproxy. In this case you define Sproxy rules to control access to the application’s API. It would likely be a single-page application. Examples are MyWatch and Juan de la Cosa

How it works

When an HTTP client makes a request, Sproxy checks for a session cookie. If it doesn’t exist (or it’s invalid, expired), it responses with HTTP status 511 with the page, where the user can choose an OAuth2 provider to authenticate with. Finally, we store the the email address in a session cookie: signed with a hash to prevent tampering, set for HTTP only (to prevent malicious JavaScript from reading it), and set it for secure (since we don’t want it traveling over plaintext HTTP connections).

From that point on, when sproxy detects a valid session cookie it extracts the email, checks it against the access rules, and relays the request to the back-end server (if allowed).


Hitting the endpoint /sproxy/logout will invalidate the session cookie. The user will be redirected to / after logout. The query parameter state can be provided to specify an alternate URL-encoded redirect path


Since all sproxied resources are private, it doesn’t make sense for web crawlers to try to index them. In fact, crawlers will index only the login page. To prevent this, sproxy returns the following for /robots.txt:

User-agent: *
Disallow: /

Permissions system

Permissions are stored in a PostgreSQL database. See sproxy.sql for details. Here are the main concepts:

  • A group is identified by a name. Every group has
    • members (identified by email address, through group_member) and
    • associated privileges (through group_privilege).
  • A privilege is identified by a name and a domain. It has associated rules (through privilege_rule) that define what the privilege gives access to.
  • A rule is a combination of sql patterns for a domain, a path and an HTTP method. A rule matches an HTTP request, if all of these components match the respective attributes of the request. However of all the matching rules only the rule with the longest path pattern will be used to determine whether a user is allowed to perform a request. This is often a bit surprising, please see the following example:

Privileges example

Consider this group_privilege and privilege_rule relations:

group privilege domain
readers basic
readers read
editors basic
editors read
editors edit
administrators basic
administrators read
administrators edit
administrators admin
privilege domain path method
basic /% GET
read /wiki/% GET
edit /wiki/edit/% %
admin /admin/% %

With this setup, everybody (that is readers, editors and administratorss) will have access to e.g. /imgs/logo.png and /favicon.ico, but only administrators will have access to /admin/index.php, because the longest matching path pattern is /admin/% and only administrators have the admin privilege.

Likewise readers have no access to e.g. /wiki/edit/delete_everything.php.

HTTP headers passed to the back-end server:

header value
From: visitor’s email address
X-Groups: all groups that granted access to this resource, separated by commas (see the note below)
X-Given-Name: the visitor’s given (first) name
X-Family-Name: the visitor’s family (last) name
X-Forwarded-Proto: the visitor’s protocol of an HTTP request, always https
X-Forwarded-For the visitor’s IP address (added to the end of the list if header is already present in client request)

X-Groups denotes an intersection of the groups the visitor belongs to and the groups that granted access:

Visitor’s groups Granted groups X-Groups
all all, devops all
all, devops all all
all, devops all, devops all,devops
all, devops devops devops
devops all, devops devops
devops all Access denied

Configuration file

By default sproxy will read its configuration from config/sproxy.yml. There is example file with documentation config/sproxy.yml.example. You can specify a custom path with:

sproxy --config /path/to/sproxy.yml



  • This is the last release of this Sproxy. See Sproxy2.
  • Google: prompt for account only, don’t ask for offline access.


  • If the user is not authenticated, show login page with HTTP status code 511, instead of 302 -> 200. It had bad UX for AJAX calls.
  • Always convert authenticated user’s email to lowercase. This affects the cookie and the From header.
  • Stop using the string-conversions package.
  • Print authentication code in debug mode only.

  • Fixed cabal source distribution


  • Added support for LinkedIn OAuth2 API. Added new options linkedin_client_id and linkedin_client_secret. They are optional as well as Google’s client_id and client_secret. The user is now redirected to the sproxy/login page to choose an OAuth2 provider.


  • Session shelf life is configurable with the session_shelf_life option in configuration file. Defaults to 30 days. It was hard-coded before.
  • Dropped dependency on logsink / logging-facade (fail to build). Log to stderr only, log levels: error, warning, info, debug. The log_target option is ignored.


  • Allow running as unprivileged user: added option user in the configuration file.
  • Default log level is debug if omitted in the configuration file.


  • Combine the multiple header fields into one “field-name: field-value” pair with a comma-separated list for the field-value (Instead of removing duplicates).
  • sproxy [ -h | --help ] works (using docopt)
  • Stop counting client parsing failures as errors.


  • Made some options in configuration file optional with reasonable default values:
    • log_target: stderr
    • listen: 443
    • redirect_http_to_https: yes if listen == 443
    • backend_address: ""
    • backend_port: 8080
  • Allow backend at UNIX socket: new option backend_socket.
  • Removed tests (unsupported).
  • Don’t build Sproxy library.


  • Deny SSLv3.
  • Removed the auth_token_key option from the config file. The token is generated randomly on startup. Restarting sproxy invalidates existing sessions.
  • Added
comments powered byDisqus