;; 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 .
(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> , ."))
;; 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)))))