/sso-proxy

Primary LanguageJavaScriptMIT LicenseMIT

SSO Proxy

This is a very simple JWT-based SSO server that uses Google as an identity provider. It was written to ease the development process of new internal apps where I work and to allow employees to log into internal apps using their business Google accounts.

The login flow is quite simple:

  • A user wants to log into an internal app
  • The app redirects the user to sso-proxy.url/sso/appname?state=randomness
  • SSO Proxy verifies that the app exists and that the state is set, then:
    • If the user is not already logged in, redirects the user to Google for authentication. The state passed to Google includes:
      • s: a state value generated by SSO Proxy
      • r: the name of the app
      • a: the state value from the app
      • h: an MD5 hash of the above
    • If the user is already logged in, asks the user to select which account they wish to use in order to log in. Selecting an account here will skip the Google login process. The user can also log in with a different account and be redirected to Google for authentication.
  • After the user authenticates with Google, they are redirected back to SSO Proxy where the state is verified and the validity of the access token is checked.
  • The user is then redirected back to the app with a JWT (valid for 30 seconds) passed in the configured query parameter.

Installation

Node.js v16+ is required.

Install the dependencies:

npm i

Configuration

Copy config.json.example to config.json and configure as needed. Save your Google OAuth key file at oauth.json. You can get this file by creating an OAuth Client on the Google API Console.

You can validate your config file using a JSON Schema validator. If you're using ajv, you can run the following command: ajv validate -s config.schema.json -d config.json --strict=false --errors=text.

  • redirects: This is where apps are configured. They are required to have the following properties:
    • url: The URL that SSO Proxy will redirect the user to.
    • secret: A secret used to sign the JWT token.
    • query: Configures what information is passed in the URL's query parameters. The following placeholder variables are available:
      • {jwt}: Will be replaced with the JWT.
      • {state} Will be replaced with the state generated by the app.
      • {oauth.*}: Will be replaced by data from Google. At the time of writing, the following details are available for use in this variable:
       {
       "id": "123456789012345678901",
       "email": "user@example.com",
       "verified_email": true,
       "name": "Example User",
       "given_name": "Example",
       "family_name": "User",
       "picture": "https://lh3.googleusercontent.com/a/ACNmcxbb2old2ilREbhdVM5S5N24ktjzWSEqM4HwUMpu=s96-c",
       "locale": "en",
       "hd": "example.com"
       }
    • jwt: Configures the data that will be put in the JWT. Must have default; overrides is optional. All query placeholder variables (with the exception of jwt) can be used here.
      • default: Specifies the default data for the JWT.
      • overrides: Allows for overriding JWT properties based on the value of other JWT properties. For example, the following override will set admin to true where the email is admin@example.com:
       "overrides": {
       	"email": {
       		"admin@example.com": {
       			"admin": true
       		}
       	}
       }
      Alternatively, you can load a file containing the override rules by setting ssoproxy:require to a relative path. Doing so allows you to control overrides that apply to multiple apps from a single file. Note that when using this method, you cannot set any other overrides.
       	"overrides": {
       		"ssoproxy:require": "overrides.config.json"
       	}
  • companyName: The name of the company using SSO Proxy. This will be shown in the page titles, as well as the root page if a user is not logged in.
  • domain: The company domain. Only accounts with the email address ending in this domain will be allowed to log into SSO Proxy.
  • port: The port SSO Proxy should run on.
  • secret: A secret used in hashes and for signing cookies.
  • callbackUrl: By default, the first callback URL in the oauth.json file will be used. This option can be used to set a different callback URL.