summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2020-04-26 13:27:50 +0300
committerLars Wirzenius <liw@liw.fi>2020-04-26 17:16:06 +0300
commitaa27a940647e867746f955772b4601e6cfce9b10 (patch)
tree58a2ef099502cffe6ec541b5eba06423ecec3822
parent3505eb1fd9460d583a0c4109b739be7e0e011854 (diff)
downloadyuck-aa27a940647e867746f955772b4601e6cfce9b10.tar.gz
Change: yuck.md to use more Subplot syntax instead of yarn
-rw-r--r--Makefile2
-rw-r--r--yuck.md332
2 files changed, 322 insertions, 12 deletions
diff --git a/Makefile b/Makefile
index bc28ba4..e4cdf8f 100644
--- a/Makefile
+++ b/Makefile
@@ -11,3 +11,5 @@ htmls = $(srcs:.md=.html)
sp-docgen $< -o $@
all: $(pdfs) $(htmls)
+
+yuck.html: yuck.yaml
diff --git a/yuck.md b/yuck.md
index b4154fd..162c585 100644
--- a/yuck.md
+++ b/yuck.md
@@ -1,5 +1,8 @@
# Overview
+Yuck may some day become an OpenID Connect and OAuth server. This is
+just a sketcth for what it might look like.
+
## Concepts
Some basic concepts in this document:
@@ -9,19 +12,23 @@ Some basic concepts in this document:
* authentication: proving your identity
* authorization: giving permission to do something
-## The protocols: OAuth 2.0 and OpenID Connect
+## The protocols: OAuth and OpenID Connect
+
+[OpenID Connect]: https://openid.net/specs/openid-connect-core-1_0.html
+[OAuth]: https://tools.ietf.org/html/rfc6749
-The OAuth 2.0 protocol is for authorization, not authentication, and
+The [OAuth][] 2.0 protocol is for authorization, not authentication, and
assumes an already existing way to authenticate users. It's mainly for
giving a service or application permission to do something on your
behalf.
-The OpenID Connect (OIDC) protocol is for authenticating yourself to
-one service or application by using a third party service. This allows
-one authentication service (or identity provider) be used for any
-number of other services or applications. Further, since the identity
-provider can keep a login session open independently of the other
-services and applications, this provides a single sign-on experience.
+The [OpenID Connect][] 1.0 (OIDC) protocol is for authenticating yourself
+to one service or application by using a third party service. This
+allows one authentication service (or identity provider) be used for
+any number of other services or applications. Further, since the
+identity provider can keep a login session open independently of the
+other services and applications, this provides a single sign-on
+experience.
## Entities involved in the protocols
@@ -38,7 +45,21 @@ The protocols involves the following entities:
access to them via a web API
* the **identity provider** (IDP), which authenticates the user
-# OIDC protocol
+# The protocols
+
+
+Yuck only supports specific versions and subsets of the protocols it
+implements, chosen based mainly from the point of security. FIXME:
+expand this with explanations of why the specific choices have been
+made and why other subsets (grants, flows) are not chosen.
+
+## The OAuth 2.0 protocol: client credentials grant
+
+See [RFC8252][] for a description of the client credentials grant.
+
+[RFC8252]: https://tools.ietf.org/html/rfc8252
+
+## The OIDC 1.0 protocol: authorization code
This augments the plain OIDC with cookies:
@@ -49,7 +70,7 @@ This augments the plain OIDC with cookies:
login session
-## Successful resource access by a logged-out user
+### Successful resource access by a logged-out user
~~~plantuml
@startuml
@@ -174,7 +195,7 @@ user <- browser : show what the user wanted
@enduml
~~~
-## Successful resource access by a logged-in user
+### Successful resource access by a logged-in user
~~~plantuml
@startuml
@@ -221,7 +242,7 @@ user <- browser : show what the user wanted
~~~
-## Successful request when an access cookie has expired
+### Successful request when an access cookie has expired
~~~plantuml
@startuml
@@ -295,11 +316,298 @@ user <- browser : show what the user wanted
~~~
+### FIXME: unhappy paths
+# Acceptance criteria
+
+This chapter captures the acceptance criteria for Yuck in the form of
+*scenarios* using the Subplot language. Subplot allows Yuck to be
+automatically verified against the criteria.
+
+## Server reports its version
+
+~~~scenario
+given a running Yuck at https://auth.example.com
+when I do GET /version without a token
+then response status code is 200
+~~~
+
+## OAuth2 client credentials grant
+
+An example request:
+
+~~~
+POST /token HTTP/1.1
+Authorization: Basic USERPASS
+Content-Type: application/x-www-form-urlencoded
+
+grant_type=client_credentials&scope=foo+bar+foobar
+~~~
+
+The `USERPASS` has the client id and secret encoded as is usual for
+[HTTP Basic authentication][].
+
+[HTTP Basic authentication]: https://en.wikipedia.org/wiki/Basic_access_authentication
+
+Yuck checks the client id and secret against known, pre-registered
+clients, and that the `grant_type` URL parameter is
+`client_credential`, and returns a signed JWT access token, if all is
+OK.
+
+~~~json
+{
+ "iss": "https://auth.example.com",
+ "sub": "",
+ "aud": "CLIENT_ID",
+ "exp": 123456,
+ "scope": "foo bar"
+}
+~~~
+
+The fields are as follows:
+
+* `sub` is empty, because this interaction involves not end-user or
+ subject
+* `iss` is the configured URL from the Yuck configuration
+* `aud` is the requesting client id
+* `exp` is the timestamp in Unix seconds when the token stops being
+ valid
+* `scope` is the space-delimieted list of scopes the client is allowed
+ to have and has requested
+
+### Get token with OAuth using client credentials
+
+~~~scenario
+given a running Yuck at https://auth.example.com
+and I am a registered OAuth client
+and I am authorized to have scope foo
+when I do POST /token with grant_type=client_credential&scope=foo+bar
+then response status code is 200
+and response has header content-type: application/json
+and response body is a valid, signed JWT token
+and token sub is empty
+and token has iss: "https://auth.example.com"
+and token has aud set to my client id
+and token expires at least 60 seconds in the future
+and token has scope: "foo"
+~~~
+
+
+### End-user interactive login
+
+This is a bit long.
+
+~~~scenario
+given a running Yuck at https://auth.example.com
+and I am registered as tomjon with password hunter2
+and I am authorized to have scope foo
+and the application is registered at https://app.example.com
+when I go to the application login page
+then response status code is 302
+and response has header Location: https://auth.example.com/auth?response_type=code&scope=openid+foo&client_id=facade&state=RANDOM&redirect_uri=https://app.example.com/callback
+
+when I follow the redirect
+then response status code is 200
+and response has header content-type: text/html
+and body HTML form has username
+and body HTML form has password
+
+when I do POST /auth with username=tomjon&password=wrong
+then response status code is 401
+
+when I do POST /auth with username=tomjon&password=hunter2
+then response status code is 302
+and response has header Location: https://app.example.com/callback?...
+and I remember the Location URL code parameter
+
+when I follow the redirect
+
+when the application requests a token using remembered code
+then response status code is 200
+and response has header content-type: application/json
+and body has field access_token
+and body has field token_type: Bearer
+and body has field expires_in
+
+and access_token is a valid, signed JWT
+and access_token has scope: "foo"
+and access_token has sub: "tomjon"
+~~~
+
+
+## Manage clients, users, applications via API
+
+~~~yarn
+SCENARIO manage clients, users, applications
+GIVEN an RSA key pair for token signing
+AND a Qvisqve configuration for "https://qvisqve.example.com"
+AND Qvisqve configuration has a token lifetime of 3600
+AND a running Qvisqve instance
+AND an access token for admin with scopes
+... uapi_clients_post
+... uapi_clients_get
+... uapi_clients_id_get
+... uapi_clients_id_put
+... uapi_clients_id_secret_put
+... uapi_clients_id_delete
+... uapi_users_post
+... uapi_users_get
+... uapi_users_id_get
+... uapi_users_id_put
+... uapi_users_id_secret_put
+... uapi_users_id_delete
+... uapi_applications_post
+... uapi_applications_get
+... uapi_applications_id_get
+... uapi_applications_id_put
+... uapi_applications_id_delete
+~~~
+First, manage clients.
+
+~~~yarn
+WHEN client requests GET /clients using token
+THEN HTTP status code is 200 OK
+AND Content-Type is application/json
+AND JSON body matches
+... {
+... "resources": []
+... }
+
+WHEN client requests POST /clients with token and body
+... {
+... "id": "james"
+... }
+THEN HTTP status code is 201 Created
+AND Location is https://qvisqve.example.com/clients/james
+
+WHEN client requests PUT /clients/james/secret with token and body
+... { "secret": "hunter2" }
+THEN HTTP status code is 200 OK
+
+WHEN client requests GET /clients using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "resources": ["james"]
+... }
+
+WHEN client requests GET /clients/james using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "id": "james"
+... }
+
+WHEN client requests DELETE /clients/james with token
+THEN HTTP status code is 200 OK
+WHEN client requests GET /clients/james using token
+THEN HTTP status code is 404 Not Found
+WHEN client requests GET /clients using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "resources": []
+... }
+~~~
+
+Then, manage users.
+
+~~~yarn
+WHEN client requests GET /users using token
+THEN HTTP status code is 200 OK
+AND Content-Type is application/json
+AND JSON body matches
+... {
+... "resources": []
+... }
+
+WHEN client requests POST /users with token and body
+... {
+... "id": "sherlock"
+... }
+THEN HTTP status code is 201 Created
+AND Location is https://qvisqve.example.com/users/sherlock
+
+WHEN client requests PUT /users/sherlock/secret with token and body
+... { "secret": "hunter2" }
+THEN HTTP status code is 200 OK
+
+WHEN client requests GET /users using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "resources": ["sherlock"]
+... }
+
+WHEN client requests GET /users/sherlock using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "id": "sherlock"
+... }
+
+WHEN client requests DELETE /users/sherlock with token
+THEN HTTP status code is 200 OK
+WHEN client requests GET /users/sherlock using token
+THEN HTTP status code is 404 Not Found
+WHEN client requests GET /users using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "resources": []
+... }
+~~~
+
+Then, manage applications.
+
+~~~yarn
+WHEN client requests GET /applications using token
+THEN HTTP status code is 200 OK
+AND Content-Type is application/json
+AND JSON body matches
+... {
+... "resources": []
+... }
+
+WHEN client requests POST /applications with token and body
+... {
+... "id": "MI6",
+... "callbacks": ["https://mi6.example.com/callback"]
+... }
+THEN HTTP status code is 201 Created
+AND Location is https://qvisqve.example.com/applications/MI6
+
+WHEN client requests GET /applications using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "resources": ["MI6"]
+... }
+
+WHEN client requests GET /applications/MI6 using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "id": "MI6",
+... "callbacks": ["https://mi6.example.com/callback"]
+... }
+
+WHEN client requests DELETE /applications/MI6 with token
+THEN HTTP status code is 200 OK
+WHEN client requests GET /applications/MI6 using token
+THEN HTTP status code is 404 Not Found
+WHEN client requests GET /applications using token
+THEN HTTP status code is 200 OK
+AND JSON body matches
+... {
+... "resources": []
+... }
+~~~
---
title: Yuck or OIDC
+bindings: yuck.yaml
...