summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorVivien Kraus <vivien@planete-kraus.eu>2021-08-07 22:45:06 +0200
committerVivien Kraus <vivien@planete-kraus.eu>2021-08-13 01:06:38 +0200
commitdb55d55e5c36c940986f437d26da1ff3c601c3b4 (patch)
tree0ecec5b2bd0b0bc6a02981a7c3b9ccafbb891c3b /tests
parent0b5d0622e11c1f919ce660893067d3121e2583a0 (diff)
Make a better client API
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/client-authorization.scm134
-rw-r--r--tests/client-token.scm137
-rw-r--r--tests/client-workflow.scm140
4 files changed, 141 insertions, 273 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 086ccbd..e09ad57 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -57,8 +57,7 @@ TESTS = %reldir%/load-library.scm \
%reldir%/token-endpoint-refresh.scm \
%reldir%/provider-confirmation.scm \
%reldir%/resource-server.scm \
- %reldir%/client-authorization.scm \
- %reldir%/client-token.scm \
+ %reldir%/client-workflow.scm \
%reldir%/client-manifest-not-modified.scm \
%reldir%/server-content.scm \
%reldir%/server-path.scm \
diff --git a/tests/client-authorization.scm b/tests/client-authorization.scm
deleted file mode 100644
index af95893..0000000
--- a/tests/client-authorization.scm
+++ /dev/null
@@ -1,134 +0,0 @@
-;; webid-oidc, implementation of the Solid specification
-;; Copyright (C) 2020, 2021 Vivien Kraus
-
-;; This program is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU Affero General Public License as
-;; published by the Free Software Foundation, either version 3 of the
-;; License, or (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU Affero General Public License for more details.
-
-;; You should have received a copy of the GNU Affero General Public License
-;; along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-(use-modules (webid-oidc client)
- (webid-oidc testing)
- ((webid-oidc stubs) #:prefix stubs:)
- (web uri)
- (web response)
- (srfi srfi-19)
- (ice-9 optargs)
- (ice-9 receive)
- (ice-9 hash-table))
-
-;; We need to test different things.
-
-;; 1. It works when passed a host
-;; 2. It works when passed a webid with foreign identity providers
-;; 3. It works when passed a webid without foreign identity providers
-
-(with-test-environment
- "client-authorization"
- (lambda ()
- (define* (http-get uri #:key (headers '()))
- (cond
- ;; 1. We pass a host name
- ((equal? uri (string->uri "https://case-1.client-authorization.scm/.well-known/openid-configuration"))
- (values
- (build-response #:headers `((content-type application/json)))
- (stubs:scm->json-string
- `((jwks_uri . "https://case-1.client-authorization.scm/keys")
- (authorization_endpoint . "https://case-1.client-authorization.scm/authorize")
- (token_endpoint . "https://case-1.client-authorization.scm/token")))))
- ;; It’s not a webid
- ((equal? uri (string->uri "https://case-1.client-authorization.scm"))
- (values
- (build-response #:code 404 #:reason-phrase "Not Found")
- #f))
- ;; 2. We first dereference the webid
- ((equal? uri (string->uri "https://case-2.client-authorization.scm/profile/card#me"))
- (values
- (build-response #:headers `((content-type text/turtle)))
- "<#me> <http://www.w3.org/ns/solid/terms#oidcIssuer> <https://one.identity.provider>, <https://another.identity.provider> ."))
- ;; and we get the config of all IPs
- ((equal? uri (string->uri "https://case-2.client-authorization.scm/.well-known/openid-configuration"))
- (values
- (build-response #:headers `((content-type application/json)))
- (stubs:scm->json-string
- `((jwks_uri . "https://case-2.client-authorization.scm/keys")
- (authorization_endpoint . "https://case-2.client-authorization.scm/authorize")
- (token_endpoint . "https://case-2.client-authorization.scm/token")))))
- ((equal? uri (string->uri "https://one.identity.provider/.well-known/openid-configuration"))
- (values
- (build-response #:headers `((content-type application/json)))
- (stubs:scm->json-string
- `((jwks_uri . "https://one.identity.provider/keys")
- (authorization_endpoint . "https://one.identity.provider/authorize")
- (token_endpoint . "https://one.identity.provider/token")))))
- ((equal? uri (string->uri "https://another.identity.provider/.well-known/openid-configuration"))
- (values
- (build-response #:headers `((content-type application/json)))
- (stubs:scm->json-string
- `((jwks_uri . "https://another.identity.provider/keys")
- (authorization_endpoint . "https://another.identity.provider/authorize")
- (token_endpoint . "https://another.identity.provider/token")))))
- ;; 3. The webid has no IPs.
- ((equal? uri (string->uri "https://case-3.client-authorization.scm/profile/card#me"))
- (values
- (build-response #:headers `((content-type text/turtle)))
- ""))
- ;; so we query the host of the webid.
- ((equal? uri (string->uri "https://case-3.client-authorization.scm/.well-known/openid-configuration"))
- (values
- (build-response #:headers `((content-type application/json)))
- (stubs:scm->json-string
- `((jwks_uri . "https://case-3.client-authorization.scm/keys")
- (authorization_endpoint . "https://case-3.client-authorization.scm/authorize")
- (token_endpoint . "https://case-3.client-authorization.scm/token")))))
- (else
- (format (current-error-port) "Unexpected GET query of URI ~a.\n" (uri->string uri))
- (exit 1))))
- (let ((case-1 (authorize "case-1.client-authorization.scm"
- #:client-id "https://app.client-authorization.scm"
- #:redirect-uri "https://app.client-authorization.scm/redirected"
- #:state "integrity&check"
- #:http-get http-get))
- (case-2 (authorize "https://case-2.client-authorization.scm/profile/card#me"
- #:client-id "https://app.client-authorization.scm"
- #:redirect-uri "https://app.client-authorization.scm/redirected"
- #:state "integrity&check"
- #:http-get http-get))
- (case-3 (authorize "https://case-3.client-authorization.scm/profile/card#me"
- #:client-id "https://app.client-authorization.scm"
- #:redirect-uri "https://app.client-authorization.scm/redirected"
- #:state "integrity&check"
- #:http-get http-get))
- (expected-1
- `(("https://case-1.client-authorization.scm"
- . ,(string->uri "https://case-1.client-authorization.scm/authorize?client_id=https%3A%2F%2Fapp.client-authorization.scm&redirect_uri=https%3A%2F%2Fapp.client-authorization.scm%2Fredirected&state=integrity%26check"))))
- (expected-2
- `(("https://case-2.client-authorization.scm"
- . ,(string->uri "https://case-2.client-authorization.scm/authorize?client_id=https%3A%2F%2Fapp.client-authorization.scm&redirect_uri=https%3A%2F%2Fapp.client-authorization.scm%2Fredirected&state=integrity%26check"))
- ("https://one.identity.provider"
- . ,(string->uri "https://one.identity.provider/authorize?client_id=https%3A%2F%2Fapp.client-authorization.scm&redirect_uri=https%3A%2F%2Fapp.client-authorization.scm%2Fredirected&state=integrity%26check"))
- ("https://another.identity.provider"
- . ,(string->uri "https://another.identity.provider/authorize?client_id=https%3A%2F%2Fapp.client-authorization.scm&redirect_uri=https%3A%2F%2Fapp.client-authorization.scm%2Fredirected&state=integrity%26check"))))
- (expected-3
- `(("https://case-3.client-authorization.scm"
- . ,(string->uri "https://case-3.client-authorization.scm/authorize?client_id=https%3A%2F%2Fapp.client-authorization.scm&redirect_uri=https%3A%2F%2Fapp.client-authorization.scm%2Fredirected&state=integrity%26check")))))
- (unless (equal? case-1 expected-1)
- (format (current-error-port) "Case 1 failed:\n~s\n~s\n\n"
- case-1 expected-1)
- (exit 2))
- (unless (equal? (hash-map->list cons (alist->hash-table case-2))
- (hash-map->list cons (alist->hash-table expected-2)))
- (format (current-error-port) "Case 2 failed:\n~s\n~s\n\n"
- case-2 expected-2)
- (exit 3))
- (unless (equal? case-3 expected-3)
- (format (current-error-port) "Case 3 failed:\n~s\n~s\n\n"
- case-3 expected-3)
- (exit 4)))))
diff --git a/tests/client-token.scm b/tests/client-token.scm
deleted file mode 100644
index 576019a..0000000
--- a/tests/client-token.scm
+++ /dev/null
@@ -1,137 +0,0 @@
-;; webid-oidc, implementation of the Solid specification
-;; Copyright (C) 2020, 2021 Vivien Kraus
-
-;; This program is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU Affero General Public License as
-;; published by the Free Software Foundation, either version 3 of the
-;; License, or (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU Affero General Public License for more details.
-
-;; You should have received a copy of the GNU Affero General Public License
-;; along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-(use-modules (webid-oidc client)
- (webid-oidc testing)
- (webid-oidc token-endpoint)
- (webid-oidc jwk)
- (webid-oidc authorization-code)
- (webid-oidc oidc-configuration)
- (webid-oidc jws)
- (webid-oidc oidc-id-token)
- ((webid-oidc parameters) #:prefix p:)
- (web uri)
- (web request)
- (web response)
- (srfi srfi-19)
- (ice-9 optargs)
- (ice-9 receive)
- (ice-9 hash-table))
-
-(with-test-environment
- "client-token"
- (lambda ()
- (define the-current-time 0)
- (parameterize ((p:current-date (lambda () the-current-time)))
- (define issuer-key (generate-key #:n-size 2048))
- (define issuer-configuration
- (make-oidc-configuration
- "https://issuer.client-token.scm/keys"
- "https://issuer.client-token.scm/authorize"
- "https://issuer.client-token.scm/token"))
- (define token-endpoint (make-token-endpoint
- (string->uri "https://issuer.client-token.scm/token")
- (string->uri "https://issuer.client-token.scm")
- 'RS256
- issuer-key
- 3600))
- (define client-key (generate-key #:n-size 2048))
- (define authorization-code
- (issue-authorization-code 'RS256 issuer-key 120
- (string->uri "https://client-token.scm/profile/card#me")
- (string->uri "https://app.client-token.scm/app#id")))
- (define* (http-get uri #:key (headers '()))
- (cond
- ((equal? uri (string->uri "https://issuer.client-token.scm/.well-known/openid-configuration"))
- (serve-oidc-configuration
- (time-utc->date (make-time time-utc 0 (+ the-current-time 3600)))
- issuer-configuration))
- ((equal? uri (string->uri "https://issuer.client-token.scm/keys"))
- (serve-jwks
- (time-utc->date (make-time time-utc 0 (+ the-current-time 3600)))
- (make-jwks (list issuer-key))))
- (else
- (format (current-error-port) "GET request to ~a: error.\n" (uri->string uri))
- (exit 1))))
- (define* (http-post uri #:key (body #f) (headers '()))
- (unless (equal? uri (oidc-configuration-token-endpoint issuer-configuration))
- (format (current-error-port)
- "Wrong URI for token negociation: ~a (expected ~a).\n"
- (uri->string uri)
- (uri->string
- (oidc-configuration-token-endpoint
- issuer-configuration)))
- (exit 2))
- (unless (equal? body (format #f "grant_type=authorization_code&code=~a"
- authorization-code))
- (format (current-error-port)
- "Wrong body: ~s\n" body)
- (exit 3))
- (unless (equal?
- (assoc-ref headers 'content-type)
- '(application/x-www-form-urlencoded))
- (format (current-error-port)
- "Wrong content type: ~s\n" (assoc-ref headers 'content-type))
- (exit 4))
- (let ((request
- (build-request uri
- #:method 'POST
- #:headers headers
- #:port (open-input-string body)))
- (request-body body))
- (receive (response response-body user error)
- (token-endpoint request request-body)
- (values response response-body))))
- (let ((response
- (token "https://issuer.client-token.scm"
- client-key
- #:authorization-code authorization-code
- #:http-get http-get
- #:http-post http-post)))
- (let ((id-token (assq-ref response 'id_token))
- (access-token (assq-ref response 'access_token))
- (token-type (assq-ref response 'token_type))
- (token-expiration (assq-ref response 'expires_in))
- (refresh-token (assq-ref response 'refresh_token)))
- (let ((id-token-dec (id-token-decode id-token #:http-get http-get))
- (access-token-dec (jws-decode access-token (lambda (jws) issuer-key))))
- (unless id-token-dec
- (format (current-error-port) "Could not decode the ID token from ~s (~s)"
- id-token response)
- (exit 5))
- (unless access-token-dec
- (format (current-error-port) "Could not decode the access token from ~s (~s)"
- access-token response)
- (exit 6))
- (unless refresh-token
- (format (current-error-port) "There does not seem to be a refresh token in ~s"
- response)
- (exit 6))
- (unless (equal? (id-token-webid id-token-dec)
- (string->uri "https://client-token.scm/profile/card#me"))
- (exit 7))
- (unless (equal? (id-token-iss id-token-dec)
- (string->uri "https://issuer.client-token.scm"))
- (exit 8))
- (unless (equal? (id-token-aud id-token-dec)
- (string->uri "https://app.client-token.scm/app#id"))
- (exit 9))
- ;; It’s not the job of the client to check that the access
- ;; token is correct; TODO: add a check with a resource
- ;; server.
-
- ;; TODO: try to negociate a refresh token.
- ))))))
diff --git a/tests/client-workflow.scm b/tests/client-workflow.scm
new file mode 100644
index 0000000..04a4455
--- /dev/null
+++ b/tests/client-workflow.scm
@@ -0,0 +1,140 @@
+;; webid-oidc, implementation of the Solid specification
+;; Copyright (C) 2021 Vivien Kraus
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU Affero General Public License as
+;; published by the Free Software Foundation, either version 3 of the
+;; License, or (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU Affero General Public License for more details.
+
+;; You should have received a copy of the GNU Affero General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+(use-modules ((webid-oidc client) #:prefix client:)
+ ((webid-oidc client accounts) #:prefix client:)
+ ((webid-oidc jwk) #:prefix jwk:)
+ (webid-oidc testing)
+ ((webid-oidc stubs) #:prefix stubs:)
+ ((webid-oidc refresh-token) #:prefix refresh:)
+ ((webid-oidc simulation) #:prefix sim:)
+ ((webid-oidc parameters) #:prefix p:)
+ (web uri)
+ (web request)
+ (web response)
+ (srfi srfi-19)
+ (srfi srfi-26)
+ (ice-9 optargs)
+ (ice-9 receive)
+ (ice-9 hash-table)
+ (ice-9 match))
+
+;; In this example, a user firsts requests an account, then logs in
+;; with a refresh token, then logs out, but we can still revive per
+;; account, then the refresh token gets banned.
+
+(define (display-log simulation)
+ (format (current-error-port) "Log:\n")
+ (for-each
+ (match-lambda
+ ((request request-body response response-body)
+ (format (current-error-port) "~s ~s (~s): ~s ~s\n"
+ (request-method request)
+ (uri->string (request-uri request))
+ request-body
+ (response-code response)
+ (response-reason-phrase response))))
+ (sim:simulation-scroll-log! simulation))
+ (exit 42))
+
+(with-test-environment
+ "client-workflow"
+ (lambda ()
+ (let ((simulation (sim:make-simulation)))
+ (sim:add-server! simulation
+ (string->uri "https://server@client-workflow.scm")
+ (string->uri "https://server@client-workflow.scm/alice#me"))
+ (sim:add-client! simulation
+ (string->uri "https://client@client-workflow.scm")
+ (string->uri "https://client@client-workflow.scm/id")
+ (string->uri "https://client@client-workflow.scm/authorized")
+ "Client workflow test"
+ (string->uri "https://client@client-workflow.scm/about"))
+ (let ((client (client:make-client
+ (string->uri "https://client@client-workflow.scm/id")
+ (jwk:generate-key #:n-size 2048)
+ (string->uri "https://client@client-workflow.scm/authorized"))))
+ (parameterize ((p:current-date 0)
+ (client:authorization-process
+ (lambda* (uri #:key issuer)
+ (sim:grant-authorization simulation uri))))
+ (receive (response response-body)
+ (let ((handler
+ (client:request client #f
+ (string->uri "https://server@client-workflow.scm")
+ #:http-request (cute sim:request simulation <...>))))
+ (handler (build-request (string->uri "https://server@client-workflow.scm/"))
+ #f))
+ (unless (eqv? (response-code response) 200)
+ ;; Only Alice can read that resource.
+ (exit 3)))
+ (match (sim:simulation-scroll-log! simulation)
+ ;; 1. The client gets the oidc configuration of the
+ ;; server.
+
+ ;; 2. The browser gets redirected to the authorization
+ ;; URI and POSTs the authorization form. The server makes
+ ;; a request to the client ID, which replies first.
+
+ ;; 3. The authorization request completes.
+
+ ;; 4. The client exchanges the authorization code for a
+ ;; refresh token.
+
+ ;; 5. and 6. The client decodes the ID token and requests
+ ;; the server keys.
+
+ ;; 7. and 8. While the client is waiting for the final response to
+ ;; complete, the server checks the access token validity by
+ ;; querying the identity provider for its key.
+
+ ;; 9. The client sends the signed request to the / URI of
+ ;; the server.
+ (((get-oidc-config-request _ get-oidc-config-response _)
+ (get-client-id-request _ get-client-id-response _)
+ (authorization-request _ authorization-response _)
+ (token-request _ token-response _)
+ _ _ ;; the client gets the key
+ _ _ ;; the resource server gets the key
+ (final-request _ final-response _))
+ (unless
+ (and
+ ;; 1. Get the authorization endpoint.
+ (equal? (request-uri get-oidc-config-request)
+ (string->uri "https://server@client-workflow.scm/.well-known/openid-configuration"))
+ (eqv? (response-code get-oidc-config-response) 200)
+ ;; 2. The server checks the client ID.
+ (equal? (request-uri get-client-id-request)
+ (string->uri "https://client@client-workflow.scm/id"))
+ (eqv? (response-code get-client-id-response) 200)
+ ;; 3. The authorization request completes.
+ (string-prefix?
+ "https://server@client-workflow.scm/authorize?"
+ (uri->string (request-uri authorization-request)))
+ (eq? (request-method authorization-request) 'POST)
+ (eqv? (response-code authorization-response) 302)
+ (string-prefix?
+ "https://client@client-workflow.scm/authorized?"
+ (uri->string (response-location authorization-response)))
+ ;; 4. Token negociation.
+ (equal? (request-uri token-request)
+ (string->uri "https://server@client-workflow.scm/token"))
+ (eqv? (response-code token-response) 200)
+ ;; 5. The final request.
+ (equal? (request-uri final-request)
+ (string->uri "https://server@client-workflow.scm/"))
+ (eqv? (response-code final-response) 200))
+ (exit 4)))))))))