# Design
## Current
### Architecture
```plaintext
-------------
| user-agent |
-------------
|
V
----|:443|------------------------------
|
V------------|
| |
|------| |
V V V
| ----------------------------
|--->| authn | CI | ... | authz |
V |--------------------------|
|--->| UI | REST API | ... |
| ----------------------------
V A
| |
|---->---->---
```
## Proposed
### Architecture
```plaintext
-------------
| 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
```plantuml
@startuml
Browser -> UI: 1. Get dashboard
UI --> Browser: Generate SAML and redirect to IdP
Browser -> IdP: 2. Deliver SAML
IdP --> Browser: 3. Redirect to Login Page
Browser -> IdP: 4. Login
IdP --> Browser: 5. Generate SAML with and redirect to UI
Browser -> UI: 6. Deliver SAML
UI -> IdP: 7. Exchange 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
```plantuml
@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`.
```json
{
"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:
```json
{
"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"
]
}
```