Skip to content

⚔️Night's Watch, the OIDC Relying Party that guards the realms

License

Notifications You must be signed in to change notification settings

iad-os/nightswatch

Repository files navigation

Night's Watch Logo

The OIDC Relying Party that guards the realms


Getting Started

Configuration

The following is the default configuration; you can use it to create your own Night's Watch configuration.

oidc:
  issuerUri: https://issuer.castle_black.com
  client_id: the_wall
  client_secret: the_key_of_the_wall
  redirect_uri: https://north.7kingdoms.com/oidc/callback
  scopes: openid profile email offline_access

# https://github.com/expressjs/cookie-session
cookie:
  name: nightswatch
  keys:
    - you_know_nothing_jon_snow
  maxAge: 24h
targets:
  path: /**
  upstream: http://httpbin.org
  routes: []
  #  - path: /_dev
  #    upstream: http://httpbin.org
  rewrite: []
  #  - match: ^/_dev
  #    rewrite: '/headers'

storage:
  kind: InMemory
  specs:
    stdTTL: 24h
server:
  # max_body_limit: 100k
  http:
    enable: true
    port: 3000
  # to be implemented
  https:
    enable: false
    port: 3001
  max_header_size: 8192
  proxy:
    # http://expressjs.com/en/guide/behind-proxies.html
    # loopback - 127.0.0.1/8, ::1/128
    # linklocal - 169.254.0.0/16, fe80::/10
    # uniquelocal - 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7
    - loopback
    - linklocal
    - uniquelocal
    # add your own reverse proxy ip or a network
    # - x.x.x.x/x
  healthchecks:
    readiness: /healthcheck/ready
    liveness: /healthcheck
    timeout: 2s
relying_party:
  on_success_redirect: /
  on_fail_redirect: /
  oidc_base_path: /oidc
  oidc_paths:
    login: /login
    callback: /callback
  rules:
    - route: /**
      methods:
        - all
  logLevel: error
  headers:
    prefix: X-AUTH
    proxy:
      access-token: tokenset.access_token
      id-token: tokenset.id_token
      expires-at: tokenset.expires_at
      expires-in: tokenset.expires_in
      sub: idtoken.sub
      name: idtoken.name
      email: idtoken.email
      family-name: idtoken.family_name
      given-name: idtoken.given_name

All of the options can be provided as ENVIRONMENT variables by applying this rule:

replace('.', '__').toUpperCase()

In order to set the HTTP headers prefixes that we're going to send to the upstream, we can use the config.yaml file:

relying_party:
  headers:
    prefix: X-AUTH

A variable named RELYING_PARTY__HEADERS__PREFIX could also be set, instead.

The mandatory configuration part is the following:

With a config.minimal.yaml

oidc:
  issuerUri: https://issuer.castle_black.com
  client_id: the_wall
  client_secret: J0n_Sn0w_is_Aeg0n_T@rg@ryen
  redirect_uri: https://north.7kingdoms.com/oidc/callback
cookie:
  keys:
    - you_know_nothing_jon_snow
targets:
  upstream: http://httpbin.org

and here is its alternative, through a .env file or environment variables:

OIDC__ISSUERURI=https://issuer.castle_black.com
OIDC__CLIENT_ID=the_wall
OIDC__CLIENT_SECRET=J0n_Sn0w_is_Aeg0n_T@rg@ryen
OIDC__REDIRECT_URI=https://north.7kingdoms.com/oidc/callback
COOKIE__KEYS_0=you_know_nothing_jon_snow
TARGET__UPSTREAM=http://httpbin.org

Run Night's Watch

You can run Night's Watch in different scenarios:

Run with node 🐳 Run with Docker 🐳 Run with Docker Compose ⎈ Run in Kubernetes: (soon)

Run with node

Night's Watch is developed using NodeJS 12; check your installed version with node --version or install it from the official website.

Once you have checked the node version, you can go ahead and clone the repository:

$ git clone https://github.com/iad-os/nightswatch.git

next, run npm install to download dependencies and, finally, finally npm start or npm run start-pretty, for a prettier console logging.

In order to pass environment variables, a .env file can be created in the checkout folder; otherwise, they can be passed with this npm start command: CONFIG_FILE=./recipes/simple/config.simple.yaml npm run start.

It is also possible to use a .yaml configuration:

$ cp /src/config.default.yaml ./config.yaml

Use your own editor to configure Night's Watch the way you need it, then run it with npm start or npm run start-pretty.

If not overridden, the CONFIG_FILE is set to ./config.yaml by default and Night's Watch will try to read your configuration from config.yaml in the current folder.

🐳 Run with Docker

The official Night's Watch docker image is available at Docker Hub iad2os/nightswatch and can be executed with the following command:

$ docker run \
-e OIDC__ISSUERURI=https://issuer.castle_black.com \
-e OIDC__CLIENT_ID=the_wall \
-e OIDC__CLIENT_SECRET=J0n_Sn0w_is_Aeg0n_T@rg@ryen \
-e OIDC__REDIRECT_URI=https://north.7kingdoms.com/oidc/callback \
-e COOKIE__KEYS_0=you_know_nothing_jon_snow \
-e TARGET__UPSTREAM=http://httpbin.org \
-p 3000:3000 \
iad2os/nightswatch

a volume mount or a .env file can also be used modifying the docker run as follows:

(with volumes)

$ docker run \
-v /path/to/config.yaml:/app/config.yaml
-p 3000:3000 \
iad2os/nightswatch

(with .env)

$ docker run \
-env /path/to/.env
-p 3000:3000 \
iad2os/nightswatch

🐳 Run with Docker Compose

Let's start creating a docker compose file, in this example scenario we will make http://httpbin.org safe with the aid of the Night's Watch. This will come in handy later, while verifying that everything works as expected.

version: '3.4'
services:
  nightswatch:
    image: iad2os/nightswatch
    volumes:
      - ./config-simple.yaml:/app/config.yaml
    environment:
      DEBUG: nightswatch:*
      CONFIG_FILE: ./config.yaml
    ports:
      - 3000:3000
    healthcheck:
      test:
        [
          'CMD',
          'wget',
          '--quiet',
          '--spider',
          'http://localhost:3000/healthcheck',
        ]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 5s

Configure Authorization Headers

With Night's Watch you can easily customize the headers passed to the Resource Server (in other words, your application).

For each request, Night's Watch will add some headers that a resource server may consume to bind the identity and other details needed to handle such request.

Here is the default headers and its configuration:

relying_party
  headers:
    prefix: X-AUTH
    proxy:
      access-token: tokenset.access_token
      id-token: tokenset.id_token
      expires-at: tokenset.expires_at
      expires-in: tokenset.expires_in
      sub: idtoken.sub
      name: idtoken.name
      email: idtoken.email
      family-name: idtoken.family_name
      given-name: idtoken.given_name

you can configure your own headers and decide what to send to the upstream, north of the Wall!

Every request comes with 3 objects:

here is a JSON example:

{
  "tokenset": {
    "access_token": "theAccessToken",
    "expires_at": 1583084430,
    "refresh_expires_in": 0,
    "refresh_token": "theRefreshToken",
    "token_type": "bearer",
    "id_token": "oidcIdToken",
    "not-before-policy": 0,
    "session_state": "aRandomId",
    "scope": "openid offline_access email profile"
  },
  "userinfo": {
    "sub": "459697e5-6c58-45d8-88f2-2a4ea5b3157a",
    "email_verified": true,
    "name": "Jeor Mormont",
    "preferred_username": "The Old Bear",
    "given_name": "Jeor",
    "family_name": "Mormont",
    "email": "theoldbear@castle_black.com"
  },
  "idtoken": {
    "jti": "88ae774f-4121-4a9a-8584-2a8d13831130",
    "exp": 1583084430,
    "nbf": 0,
    "iat": 1583084370,
    "iss": "https://issuer.castle_black.com",
    "aud": "1min-access-token",
    "sub": "459697e5-6c58-45d8-88f2-2a4ea5b3157a",
    "typ": "ID",
    "azp": "1min-access-token",
    "auth_time": 1583084369,
    "session_state": "99bd9ca8-cd06-469c-90ea-4fddcc3bcdee",
    "acr": "1",
    "email_verified": true,
    "name": "Jeor Mormont",
    "preferred_username": "The Old Bear",
    "given_name": "Jeor",
    "family_name": "Mormont",
    "email": "theoldbear@castle_black.com"
  }
}

Adding a custom Header (sample)

For instance, you could add a header named X-AUTH-ROLES, representing user roles. using an environment variable:

RELYING_PARTY__HEADERS_PROXY_ROLES=idtoken.roles

using config.yaml

relying_party
  headers:
    prefix: X-AUTH
    proxy:
      roles: idtoken.roles

⚠️ IdToken may differ between issuer; which claim to include can also be configured. Please refer to your IDP documentation.

The Token Set claims

An object named tokenset will be available with the following properties:

access_token: <string>
token_type: <string>
id_token: <string>
refresh_token: <string>
expires_in: <number>
expires_at: <number> Access token expiration timestamp, formed by the number of seconds since the epoch (January 1, 1970 00:00:00 UTC).
session_state: <string>
other properties may be present and they'll be passthrough available on the TokenSet instance

This is an example of TokenSet object:

tokenset
 access_token: theAccessToken
 expires_at: 1583084430
 refresh_expires_in: 0
 refresh_token: theRefreshToken
 token_type: bearer
 id_token: oidcIdToken
 not fore-policy: 0
 session_state: aRandomId
 scope: openid offline_access email profile

for more details, check the official TokenSet documentation at panva/node-openid-client.

The UserInfo claims

The UserInfo object contains the claims defined by the OIDC standards, and this can change between OIDC providers. If you also control the OIDC provider, consult the documentation to configure what claims are included in the UserInfo endpoint. More info about UserInfo and standard claims can be found at OIDC Specs - User Info OIDC Specs - Standard Claims

This is an UserInfo object example:

userinfo:
  sub: 459697e5-6c58-45d8-88f2-2a4ea5b3157a
  email_verified": true
  name: Jeor Mormont
  preferred_username: The Old Bear
  given_name: Jeor
  family_name: Mormont
  email: theoldbear@castle_black.com

The IdToken claims

Even though you have a serialized and JWT-signed id_token located at the tokenset.id_token level, the root-level idtoken object contains the same content but, indeed, as an object. With this, it will be more convenient to handle claims in the headers..

Here is an example of idtoken object:

idtoken:
  jti: 88ae774f-4121-4a9a-8584-2a8d13831130
  exp: 1583084430
  nbf: 0
  iat: 1583084370
  iss: https://issuer.castle_black.com
  sub: 459697e5-6c58-45d8-88f2-2a4ea5b3157a
  typ: ID
  azp: 1min-access-token
  auth_time: 1583084369
  session_state: 99bd9ca8-cd06-469c-90ea-4fddcc3bcdee
  acr: 1
  email_verified: true
  name: Jeor Mormont
  preferred_username: The Old Bear
  given_name: Jeor
  family_name: Mormont
  email: theoldbear@castle_black.com

Check out the official OIDC docs: OIDC Specs

About

⚔️Night's Watch, the OIDC Relying Party that guards the realms

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •