main

Design

Current

Architecture

 -------------
 | user-agent |
 -------------
       |
       V
----|:443|------------------------------
       |
       V------------|
       |            |
       |------|     |
       V      V     V
       |    ----------------------------
       |--->| authn | CI | ... | authz |
       V    |--------------------------|
       |--->| UI | REST API | ...      |
       |    ----------------------------
       V            A
       |            |
       |---->---->---

Proposed

Architecture

 -------------
 | user-agent |
 -------------
       |
       V
----|:8080|-----------------------------------------------
       |
       V
 ---------------
 | API Gateway |
 ---------------
        |
        |    ---------------------------------
        |    | IdP (saml, oidc)              |
        |    |-------------------------------|
        |--->| :http (authn) | :grpc (authz) |
        |    ---------------------------------
        |                        A
   -----------                   |
   |         |                   |
   V         V                   |
 ------    ------------          |
 | UI |    | REST API |----------|
 ------    ------------          |
   |                             A
   |---->----------->------------|

[UI]: ui.example.com
[REST API]: api.example.com
[IdP]: idp.example.com

SAML Login Flow

@startuml
Browser -> UI: 1. Get dashboard
UI --> Browser: Generate SAML <AuthnRequest /> and redirect to IdP

Browser -> IdP: 2. Deliver SAML <AuthnRequest />
IdP --> Browser: 3. Redirect to Login Page
Browser -> IdP: 4. Login
IdP --> Browser: 5. Generate SAML <AuthnResponse /> with <Assertion /> and redirect to UI

Browser -> UI: 6. Deliver SAML <AuthnResponse />
UI -> IdP: 7. Exchange <Assertion /> for Tokens
IdP --> UI: Return `access_token` and `refresh_token`
UI --> Browser: Redirect to dashboard
Browser -> UI: Get dashboard
UI -> API: 8. Request list of groups and provide Access Token
API -> IdP: 9. Check if token is valid and check declarative policy
IdP --> API: Return result of `Ability.allowed?`
API --> UI: Return list of groups as JSON
UI --> Browser: Return list of groups as HTML
@enduml
  1. GET http://ui.example.com/saml/new
  2. POST http://idp.example.com/saml/new
  3. GET http://idp.example.com/sessions/new?redirect_back=/saml/continue
  4. POST http://idp.example.com/sessions
  5. GET http://idp.example.com/saml/continue
  6. POST http://ui.example.com/saml/assertions
  7. POST http://idp.example.com/oauth/token
  8. GET http://api.example.com/groups.json
  9. GET grpc://idp.example.com/twirp/authx.rpc.Ability/Allowed

OIDC Login Flow

@startuml
Browser -> UI: 1. Get dashboard
UI --> Browser: Generate OAuth Grant Request and redirect to IdP

Browser -> IdP: 2. Deliver OAuth Grant Request
IdP --> Browser: 3. Redirect to Login Page
Browser -> IdP: 4. Login
IdP --> Browser: 5. Generate Consent Screen for Authorization Code flow
Browser -> IdP: 6. Consent
IdP --> Browser: Generate Authorization Code and redirect to UI

Browser -> UI: 7. Deliver Authorization Code Grant
UI -> IdP: 8. Exchange Authorization Code Grant for Tokens
IdP --> UI: Return `access_token` and `refresh_token`
UI --> Browser: Redirect to dashboard
Browser -> UI: Get dashboard
UI -> API: 9. Request list of groups and provide Access Token
API -> IdP: 10. Check if token is valid and check declarative policy
IdP --> API: Return result of `Ability.allowed?`
API --> UI: Return list of groups as JSON
UI --> Browser: Return list of groups as HTML
@enduml
  1. GET http://ui.example.com/oidc/new
  2. GET http://idp.example.com/oauth/authorize
  3. GET http://idp.example.com/sessions/new?redirect_back=/oauth/authorize/continue
  4. POST http://idp.example.com/sessions
  5. GET http://idp.example.com/oauth/authorize/continue
  6. POST http://idp.example.com/oauth/authorize
  7. GET http://ui.example.com/oauth/callback
  8. POST http://idp.example.com/oauth/token
  9. GET http://api.example.com/groups.json
  10. GET grpc://idp.example.com/twirp/authx.rpc.Ability/Allowed

Permissions

Option 1

permission scope description
read gid://app/Organization/1 Can read Org 1 resource
read gid://app/Organization/1/* Can read every resource below Org 1 hierarchy
read gid://app/Organization/1/Group/1 Can read Group 1 resource
read gid://app/Organization/1/Group/1/* Can read every resource below Group 1 hierarchy
read gid://app/Organization/1/Group/1/Project/1 Can read project 1
read gid://app/Project/1 Can read project 1 resource (short circuit example)
read gid://app/Organization/1/Group/1?attributes[]=name&attributes[]=description Can read name and description of Group 1 resource

Example:

The following example allows the subject of the token to read all of the descendant resources of Project 1 and Project 2 and it can read Project 3.

{
  "sub": "gid://User/17",
  "scope": {
    "read": [
      "gid://app/Organization/1/Group/1/Project/1/*",
      "gid://app/Organization/1/Group/1/Project/2/*",
      "gid://app/Organization/1/Group/2/Project/3"
    ]
  }
}

Option 2

Encode access and scope directly into the name of the permission.

permission description
read:organization:1 Can read Org 1 resource
read:organization:1:* Can read every resource below Org 1 hierarchy
read:organization:1:group:* Can read Group 1 resource
read:organization:1:group:1:* Can read every resource below Group 1 hierarchy
read:organization:1:group:1:project:1 Can read project 1
read:project:1 Can read project 1 resource (short circuit example)
read:organization:1:group:1:attributes[]=name&attributes[]=description Can read name and description of Group 1 resource

Example:

{
  "sub": "gid://User/17",
  "scope": [
    "read:organization:1:group:1:project:1:*",
    "read:organization:1:group:1:project:2:*",
    "read:organization:1:group:2:project:3"
  ]
}