summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVivien Kraus <vivien@planete-kraus.eu>2021-08-01 14:51:28 +0200
committerVivien Kraus <vivien@planete-kraus.eu>2021-08-01 18:08:56 +0200
commitbae1843f1a1d644fb3bd4f8c40b1dbb900aa3325 (patch)
tree00f590033af904a6a493e41bdebe9b3ddd73043b /src
parentd8c2ca930673da858d63f2dea9526c259a2dd936 (diff)
Use guile parameters
With parameters, the API does not need to care about the directory where to load files and how to get the time.
Diffstat (limited to 'src')
-rw-r--r--src/scm/webid-oidc/Makefile.am6
-rw-r--r--src/scm/webid-oidc/access-token.scm9
-rw-r--r--src/scm/webid-oidc/authorization-code.scm43
-rw-r--r--src/scm/webid-oidc/authorization-endpoint.scm15
-rw-r--r--src/scm/webid-oidc/cache.scm171
-rw-r--r--src/scm/webid-oidc/client.scm110
-rw-r--r--src/scm/webid-oidc/dpop-proof.scm90
-rw-r--r--src/scm/webid-oidc/hello-world.scm2
-rw-r--r--src/scm/webid-oidc/identity-provider.scm6
-rw-r--r--src/scm/webid-oidc/jti.scm58
-rw-r--r--src/scm/webid-oidc/oidc-id-token.scm8
-rw-r--r--src/scm/webid-oidc/parameters.scm34
-rw-r--r--src/scm/webid-oidc/program.scm247
-rw-r--r--src/scm/webid-oidc/refresh-token.scm61
-rw-r--r--src/scm/webid-oidc/resource-server.scm410
-rw-r--r--src/scm/webid-oidc/reverse-proxy.scm89
-rw-r--r--src/scm/webid-oidc/server/resource/content.scm31
-rw-r--r--src/scm/webid-oidc/server/resource/path.scm10
-rw-r--r--src/scm/webid-oidc/stubs.scm8
-rw-r--r--src/scm/webid-oidc/testing.scm28
-rw-r--r--src/scm/webid-oidc/token-endpoint.scm202
21 files changed, 777 insertions, 861 deletions
diff --git a/src/scm/webid-oidc/Makefile.am b/src/scm/webid-oidc/Makefile.am
index acd9ec9..3e92bd3 100644
--- a/src/scm/webid-oidc/Makefile.am
+++ b/src/scm/webid-oidc/Makefile.am
@@ -46,7 +46,8 @@ dist_webidoidcmod_DATA += \
%reldir%/rdf-index.scm \
%reldir%/http-link.scm \
%reldir%/offloading.scm \
- %reldir%/catalog.scm
+ %reldir%/catalog.scm \
+ %reldir%/parameters.scm
webidoidcgo_DATA += \
%reldir%/errors.go \
@@ -80,7 +81,8 @@ webidoidcgo_DATA += \
%reldir%/rdf-index.go \
%reldir%/http-link.go \
%reldir%/offloading.go \
- %reldir%/catalog.go
+ %reldir%/catalog.go \
+ %reldir%/parameters.go
EXTRA_DIST += %reldir%/ChangeLog
diff --git a/src/scm/webid-oidc/access-token.scm b/src/scm/webid-oidc/access-token.scm
index 9c57326..acdc56f 100644
--- a/src/scm/webid-oidc/access-token.scm
+++ b/src/scm/webid-oidc/access-token.scm
@@ -20,6 +20,7 @@
#:use-module (webid-oidc jwk)
#:use-module (webid-oidc oidc-configuration)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (web client)
#:use-module (ice-9 optargs)
@@ -206,8 +207,7 @@
(alg #f)
(webid #f)
(iss #f)
- (iat #f)
- (exp #f)
+ (validity 3600)
(client-key #f)
(cnf/jkt #f)
(client-id #f))
@@ -216,6 +216,7 @@
(access-token-encode
(make-access-token
`((alg . ,(if (symbol? alg) (symbol->string alg) alg)))
- (make-access-token-payload
- webid iss iat exp cnf/jkt client-id))
+ (let ((iat (time-second (date->time-utc ((p:current-date))))))
+ (make-access-token-payload
+ webid iss iat (+ iat validity) cnf/jkt client-id)))
issuer-key))
diff --git a/src/scm/webid-oidc/authorization-code.scm b/src/scm/webid-oidc/authorization-code.scm
index 3a0da3b..267d67a 100644
--- a/src/scm/webid-oidc/authorization-code.scm
+++ b/src/scm/webid-oidc/authorization-code.scm
@@ -19,6 +19,7 @@
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module (webid-oidc jws)
#:use-module (webid-oidc jti)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (srfi srfi-19))
@@ -129,28 +130,26 @@
(string->uri
(assq-ref (the-authorization-code-payload code) 'client_id)))
-(define-public (authorization-code-decode current-time jti-list str jwk)
- (when (date? current-time)
- (set! current-time (date->time-utc current-time)))
- (when (time? current-time)
- (set! current-time (time-second current-time)))
- (with-exception-handler
- (lambda (error)
- (raise-cannot-decode-authorization-code str error))
- (lambda ()
- (let ((code (the-authorization-code (jws-decode str (lambda (x) jwk)))))
- (let ((exp (time-second (date->time-utc (authorization-code-exp code)))))
- (unless (<= current-time exp)
- (raise-authorization-code-expired exp current-time))
- (unless (jti-check current-time (authorization-code-jti code)
- jti-list
- (- exp current-time))
- (with-exception-handler
- (lambda (error)
- (raise-jti-found (authorization-code-jti code) error))
- (lambda ()
- (error "the jti-check function returned #f"))))
- code)))))
+(define-public (authorization-code-decode str jwk)
+ (parameterize ((p:current-date
+ (time-second (date->time-utc ((p:current-date))))))
+ (with-exception-handler
+ (lambda (error)
+ (raise-cannot-decode-authorization-code str error))
+ (lambda ()
+ (let ((code (the-authorization-code (jws-decode str (lambda (x) jwk)))))
+ (let ((exp (time-second (date->time-utc (authorization-code-exp code))))
+ (current-time (time-second (date->time-utc ((p:current-date))))))
+ (unless (<= current-time exp)
+ (raise-authorization-code-expired exp current-time))
+ (unless (jti-check (authorization-code-jti code)
+ (- exp current-time))
+ (with-exception-handler
+ (lambda (error)
+ (raise-jti-found (authorization-code-jti code) error))
+ (lambda ()
+ (error "the jti-check function returned #f"))))
+ code))))))
(define-public (authorization-code-encode authorization-code key)
(with-exception-handler
diff --git a/src/scm/webid-oidc/authorization-endpoint.scm b/src/scm/webid-oidc/authorization-endpoint.scm
index 9ff994c..d5591b7 100644
--- a/src/scm/webid-oidc/authorization-endpoint.scm
+++ b/src/scm/webid-oidc/authorization-endpoint.scm
@@ -20,6 +20,7 @@
#:use-module (webid-oidc jwk)
#:use-module (webid-oidc authorization-code)
#:use-module (webid-oidc client-manifest)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (web client)
#:use-module (web request)
@@ -35,8 +36,7 @@
(define*-public (make-authorization-endpoint subject encrypted-password alg jwk validity
#:key
- (http-get http-get)
- (current-time current-time))
+ (http-get http-get))
(define (parse-arg x decode-plus-to-space?)
(map (lambda (x) (uri-decode
x
@@ -90,16 +90,9 @@
(lambda (error)
(error-application locale error))
(lambda ()
- (let* ((current-time (if (thunk? current-time)
- (current-time)
- current-time))
+ (let* ((current-time ((p:current-date))) ;; current-date is a thunk parameter
(current-sec
- (cond ((date? current-time)
- (time-second (date->time-utc current-time)))
- ((time? current-time)
- (time-second current-time))
- ((integer? current-time)
- current-time)))
+ (time-second (date->time-utc current-time)))
(exp-sec (+ current-sec validity))
(exp (time-utc->date (make-time time-utc 0 exp-sec)))
(code (issue-authorization-code alg jwk exp subject client-id)))
diff --git a/src/scm/webid-oidc/cache.scm b/src/scm/webid-oidc/cache.scm
index dbf0112..e98f87f 100644
--- a/src/scm/webid-oidc/cache.scm
+++ b/src/scm/webid-oidc/cache.scm
@@ -16,6 +16,7 @@
(define-module (webid-oidc cache)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web client)
#:use-module (web request)
#:use-module (web response)
@@ -24,7 +25,17 @@
#:use-module (ice-9 receive)
#:use-module (ice-9 optargs)
#:use-module (srfi srfi-19)
- #:use-module (rnrs bytevectors))
+ #:use-module (rnrs bytevectors)
+ #:export
+ (
+ clean-cache
+ add
+ read
+ varies?
+ valid?
+ revalidate
+ with-cache
+ ))
;; The cache follows the recommendations of
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
@@ -48,36 +59,26 @@
;; There is a global lock file at the root of the cache, which serves
;; for region locking. Do not remove it!
-(define (default-cache-dir)
- (let ((xdg-cache-home
- (or (getenv "XDG_CACHE_HOME")
- (format #f "~a/.cache" (getenv "HOME")))))
- (format #f "~a/disfluid" xdg-cache-home)))
+(define (web-cache-dir)
+ (string-append (p:cache-home) "/web-cache/"))
-(define (web-cache-dir dir)
- (when (thunk? dir)
- (set! dir (dir)))
- (string-append dir
- "/web-cache/"))
-
-(define (file-name uri dir)
+(define (file-name uri)
(when (string? uri)
(set! uri (string->uri uri)))
- (string-append (web-cache-dir dir)
+ (string-append (web-cache-dir)
(stubs:hash 'SHA-256 (uri->string uri))))
-(define (lock-file-name dir)
- (string-append (web-cache-dir dir) ".lock"))
+(define (lock-file-name)
+ (string-append (web-cache-dir) ".lock"))
(define (event? percents)
(<= (* (random:uniform) 100)
percents))
-(define*-public (clean-cache
- #:key
- (percents 5)
- (dir default-cache-dir))
- (define lock-file (lock-file-name dir))
+(define* (clean-cache
+ #:key
+ (percents 5))
+ (define lock-file (lock-file-name))
(define (survives?)
(not (event? percents)))
(define (enter? name stat result)
@@ -104,14 +105,13 @@
name (strerror errno))
result)
(file-system-fold enter? leaf down up skip error 0
- (web-cache-dir dir)))
+ (web-cache-dir)))
(define (maybe-clean-cache
pc-happen
- pc-cleaned
- dir)
+ pc-cleaned)
(when (event? pc-happen)
- (clean-cache #:percents pc-cleaned #:dir dir)))
+ (clean-cache #:percents pc-cleaned)))
(define (remove-uncacheable-headers response)
(let ((headers (response-headers response)))
@@ -129,16 +129,15 @@
#:headers filtered
#:port #f))))
-(define*-public (add request response response-body
- #:key (dir default-cache-dir))
+(define (add request response response-body)
;; Don’t store it if there’s a cache-control no-store
(unless
(let ((cc (response-cache-control response '())))
(assq-ref cc 'no-store))
(set! response (remove-uncacheable-headers response))
- (let ((final-file-name (file-name (request-uri request) dir))
- (lock-file (lock-file-name dir)))
- (maybe-clean-cache 5 5 dir)
+ (let ((final-file-name (file-name (request-uri request)))
+ (lock-file (lock-file-name)))
+ (maybe-clean-cache 5 5)
(stubs:atomically-update-file
final-file-name
lock-file
@@ -152,14 +151,8 @@
(write-response-body file-response response-body))
#t))))))
-(define (the-current-time)
- (time-utc->date
- (current-time)))
-
-(define*-public (read uri
- #:key
- (dir default-cache-dir))
- (let ((final-file-name (file-name uri dir)))
+(define (read uri)
+ (let ((final-file-name (file-name uri)))
(catch 'system-error
(lambda ()
(call-with-input-file final-file-name
@@ -183,60 +176,46 @@
(or (varies-header? request-a request-b (car headers))
(varies-any-header? request-a request-b (cdr headers)))))
-(define-public (varies? request-a request-b response)
+(define (varies? request-a request-b response)
(let ((vary (response-vary response)))
(or (eq? vary '*)
(varies-any-header? request-a request-b vary))))
-(define*-public (valid? response
- #:key
- (current-time the-current-time))
- (when (thunk? current-time)
- (set! current-time (current-time)))
- (when (integer? current-time)
- (set! current-time
- (make-time time-utc 0 current-time)))
- (when (time? current-time)
- (set! current-time (time-utc->date current-time)))
- (set! current-time
- (date->time-utc current-time))
- (set! current-time
- (time-second current-time))
- (let ((cc (response-cache-control response #f))
- (date (response-date response
- (time-utc->date
- (make-time time-utc 0 current-time))))
- (last-modified (response-last-modified response)))
- (set! date (date->time-utc date))
- (set! date (time-second date))
- (when last-modified
- (set! last-modified (date->time-utc last-modified))
- (set! last-modified (time-second last-modified)))
- (if cc
- ;; Use cache-control
- (let ((cc-no-cache (assq-ref cc 'no-cache))
- (cc-no-store (assq-ref cc 'no-store))
- (cc-max-age
- (or (assq-ref cc 'max-age)
- ;; Heuristic freshness
- (and last-modified
- (/ (- date last-modified) 10)))))
- (and (not cc-no-cache)
- (not cc-no-store)
- cc-max-age
- (>= (+ date cc-max-age) current-time)))
- ;; Use expires
- (let ((exp (response-expires response)))
- (when exp
- (set! exp (date->time-utc exp))
- (set! exp (time-second exp)))
- (and exp
- (>= exp current-time))))))
+(define (valid? response)
+ ;; current-date is a thunk parameter
+ (let* ((current-date ((p:current-date)))
+ (current-time (time-second (date->time-utc current-date))))
+ (let ((cc (response-cache-control response #f))
+ (date (time-second (date->time-utc (response-date response current-date))))
+ (last-modified
+ (let ((as-date (response-last-modified response)))
+ (and as-date
+ (time-second (date->time-utc as-date))))))
+ (if cc
+ ;; Use cache-control
+ (let ((cc-no-cache (assq-ref cc 'no-cache))
+ (cc-no-store (assq-ref cc 'no-store))
+ (cc-max-age
+ (or (assq-ref cc 'max-age)
+ ;; Heuristic freshness
+ (and last-modified
+ (/ (- date last-modified) 10)))))
+ (and (not cc-no-cache)
+ (not cc-no-store)
+ cc-max-age
+ (>= (+ date cc-max-age) current-time)))
+ ;; Use expires
+ (let ((exp
+ (let ((as-date (response-expires response)))
+ (and as-date
+ (time-second (date->time-utc as-date))))))
+ (and exp
+ (>= exp current-time)))))))
-(define*-public (revalidate uri response body
- #:key
- (headers '())
- (http-get http-get))
+(define* (revalidate uri response body
+ #:key
+ (headers '())
+ (http-get http-get))
(define (keep-header? h)
(case (car h)
((if-none-match if-unmodified-since) #f)
@@ -266,20 +245,14 @@
(values new-response new-response-body)))
(http-get uri #:headers headers))))
-(define*-public (with-cache
- #:key
- (current-time the-current-time)
- (http-get http-get)
- (dir default-cache-dir))
+(define* (with-cache #:key (http-get http-get))
(lambda* (uri #:key (headers '()))
(when (string? uri)
(set! uri (string->uri uri)))
- (let ((dir (if (thunk? dir) (dir) dir))
- (request (build-request uri #:headers headers)))
- (receive (stored-request stored-response body)
- (read uri #:dir dir)
+ (let ((request (build-request uri #:headers headers)))
+ (receive (stored-request stored-response body) (read uri)
(if stored-response
- (let ((valid (valid? stored-response #:current-time the-current-time))
+ (let ((valid (valid? stored-response))
(invariant (not (varies? request stored-request stored-response))))
(unless invariant
(format (current-error-port) "Cache entry for ~a varies.\n" (uri->string uri)))
@@ -289,9 +262,9 @@
(revalidate uri stored-response body
#:headers headers
#:http-get http-get)
- (add request final-response final-body #:dir dir)
+ (add request final-response final-body)
(values final-response final-body))))
(receive (final-response final-body)
(http-get uri #:headers headers)
- (add request final-response final-body #:dir dir)
+ (add request final-response final-body)
(values final-response final-body)))))))
diff --git a/src/scm/webid-oidc/client.scm b/src/scm/webid-oidc/client.scm
index e8796c0..67928db 100644
--- a/src/scm/webid-oidc/client.scm
+++ b/src/scm/webid-oidc/client.scm
@@ -21,8 +21,8 @@
#:use-module (webid-oidc oidc-id-token)
#:use-module (webid-oidc dpop-proof)
#:use-module (webid-oidc jwk)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
- #:use-module ((webid-oidc refresh-token) #:prefix refresh:)
#:use-module ((webid-oidc config) #:prefix cfg:)
#:use-module (web uri)
#:use-module (web client)
@@ -174,28 +174,17 @@
(raise-no-provider-candidates host-or-webid candidate-errors))
final-candidates))))))
-(define the-current-time current-time)
-
(define*-public (token host client-key
#:key
(authorization-code #f)
(refresh-token #f)
(http-get http-get)
- (http-post http-post)
- (current-time #f))
+ (http-post http-post))
(unless (or authorization-code refresh-token)
(scm-error 'wrong-type-arg "token"
"You need to either set #:authorization-code or #:refresh-token."
'()
(list authorization-code)))
- (unless current-time
- (set! current-time the-current-time))
- (when (thunk? current-time)
- (set! current-time (current-time)))
- (when (integer? current-time)
- (set! current-time (make-time time-utc 0 current-time)))
- (when (time? current-time)
- (set! current-time (time-utc->date current-time)))
(let ((token-endpoint
(oidc-configuration-token-endpoint
(get-oidc-configuration host #:http-get http-get)))
@@ -212,8 +201,7 @@
(else
(error "Unknown key type of ~S." client-key)))
#:htm 'POST
- #:htu token-endpoint
- #:iat current-time)))
+ #:htu token-endpoint)))
(receive (response response-body)
(http-post token-endpoint
#:body
@@ -253,17 +241,7 @@
(raise-unexpected-header-value 'content-type (response-content-type response)))
(stubs:json-string->scm response-body)))))))))
-(define (default-dir)
- (let ((xdg-data-home
- (or
- (getenv "XDG_DATA_HOME")
- (format #f "~a/.local/share"
- (getenv "HOME")))))
- (format #f "~a/disfluid" xdg-data-home)))
-
-(define*-public (list-profiles #:key (dir default-dir))
- (when (thunk? dir)
- (set! dir (dir)))
+(define-public (list-profiles)
(map (lambda (profile)
(list
(string->uri (car profile)) ;; webid
@@ -272,20 +250,17 @@
(cadddr profile))) ;; key
(catch #t
(lambda ()
- (call-with-input-file (string-append dir "/profiles")
+ (call-with-input-file (string-append (p:data-home) "/profiles")
read))
(lambda error
(format (current-error-port) "Could not read profiles: ~s\n" error)
'()))))
-(define* (add-profile webid issuer refresh-token key
- #:key (dir default-dir))
- (when (thunk? dir)
- (set! dir (dir)))
- (let ((other-profiles (list-profiles #:dir dir)))
+(define (add-profile webid issuer refresh-token key)
+ (let ((other-profiles (list-profiles)))
(stubs:atomically-update-file
- (string-append dir "/profiles")
- (string-append dir "/profiles.lock")
+ (string-append (p:data-home) "/profiles")
+ (string-append (p:data-home) "/profiles.lock")
(lambda (port)
(write
(map (lambda (profile)
@@ -304,12 +279,8 @@
#:key
(client-id #f)
(redirect-uri #f)
- (dir default-dir)
(http-get http-get)
- (http-post http-post)
- (current-time #f))
- (when (thunk? dir)
- (set! dir (dir)))
+ (http-post http-post))
(let ((host/webid (get-host/webid)))
(let ((authorization-uris
(authorize host/webid
@@ -324,8 +295,7 @@
(token host/webid key
#:authorization-code authz-code
#:http-get http-get
- #:http-post http-post
- #:current-time current-time)))
+ #:http-post http-post)))
(let ((id-token (id-token-decode (assq-ref params 'id_token)
#:http-get http-get))
(access-token (assq-ref params 'access_token))
@@ -335,16 +305,13 @@
(add-profile (id-token-webid id-token)
(id-token-iss id-token)
refresh-token
- key
- #:dir dir))
+ key))
(values (cdr id-token) access-token key)))))))))
(define*-public (login webid issuer refresh-token key
#:key
- (dir default-dir)
(http-get http-get)
- (http-post http-post)
- (current-time #f))
+ (http-post http-post))
(when (string? webid)
(set! webid (string->uri webid)))
(when (string? issuer)
@@ -354,8 +321,7 @@
(token iss-host key
#:refresh-token refresh-token
#:http-get http-get
- #:http-post http-post
- #:current-time current-time)))
+ #:http-post http-post)))
(let ((id-token (id-token-decode (assq-ref params 'id_token)
#:http-get http-get))
(access-token (assq-ref params 'access_token))
@@ -366,30 +332,25 @@
(add-profile (id-token-webid id-token)
(id-token-iss id-token)
refresh-token
- key
- #:dir dir))
+ key))
(values (cdr id-token) access-token key)))))
(define*-public (refresh id-token
key
#:key
- (dir default-dir)
(http-get http-get)
- (http-post http-post)
- (current-time #f))
- (when (thunk? dir)
- (set! dir (dir)))
+ (http-post http-post))
(when (id-token-payload? id-token)
;; For convenience, we’d like a full ID token to use the ID token
;; API.
(set! id-token (cons `((alg . "HS256")) id-token)))
- (let ((profiles (list-profiles #:dir dir)))
+ (let ((profiles (list-profiles)))
(letrec ((find-refresh-token
(lambda (profiles)
(when (null? profiles)
(raise-profile-not-found (id-token-webid id-token)
(id-token-iss id-token)
- dir))
+ (p:data-home)))
(let ((prof (car profiles))
(others (cdr profiles)))
(let ((webid (car prof))
@@ -403,15 +364,12 @@
(id-token-iss id-token)
(find-refresh-token (profiles))
key
- #:dir dir
#:http-get http-get
- #:http-post http-post
- #:current-time current-time))))
+ #:http-post http-post))))
(define* (renew-if-expired id-token access-token key
date
#:key
- (dir default-dir)
(http-get http-get)
(http-post http-post))
;; Since we’re not supposed to decode the access token, we’re
@@ -427,25 +385,22 @@
(set! exp (date->time-utc exp))
(set! exp (time-second exp))
(if (>= date exp)
- (refresh id-token key
- #:dir dir
- #:http-get http-get
- #:http-post http-post
- #:current-time date)
+ (parameterize ((p:current-date (lambda () date)))
+ (refresh id-token key
+ #:http-get http-get
+ #:http-post http-post))
(values id-token access-token key))))
(define*-public (make-client id-token access-token key
#:key
- (dir default-dir)
(http-get http-get)
(http-post http-post)
- (http-request http-request)
- (current-time the-current-time))
+ (http-request http-request))
;; HACK: guile does not support other authentication schemes in
;; WWW-Authenticate than Basic, so it will crash when a response
;; containing that header will be issued.
(declare-header! "WWW-Authenticate" string->symbol symbol? write)
- (define (handler uri method headers other-args current-time retry?)
+ (define (handler uri method headers other-args retry?)
(let ((proof (issue-dpop-proof
key
#:alg (case (kty key)
@@ -455,7 +410,6 @@
(error "Unknown key type of ~S." key)))
#:htm method
#:htu uri
- #:iat current-time
#:access-token access-token)))
(receive (response response-body)
(apply http-request uri
@@ -470,7 +424,6 @@
;; Maybe the access token has expired?
(receive (new-id-token new-access-token new-key)
(renew-if-expired id-token access-token key server-date
- #:dir dir
#:http-get http-get
#:http-post http-post)
(if (equal? access-token new-access-token)
@@ -481,18 +434,11 @@
(set! id-token new-id-token)
(set! access-token new-access-token)
(set! key new-key)
- (handler uri method headers other-args current-time #f))))
+ (handler uri method headers other-args #f))))
(values response response-body))))))
(define (parse-args uri method headers other-args-rev rest)
(if (null? rest)
- (let ((the-current-time current-time))
- (when (thunk? the-current-time)
- (set! the-current-time (the-current-time)))
- (when (integer? the-current-time)
- (set! the-current-time (make-time time-utc 0 the-current-time)))
- (when (time? the-current-time)
- (set! the-current-time (time-utc->date the-current-time)))
- (handler uri method headers (reverse other-args-rev) the-current-time #t))
+ (handler uri method headers (reverse other-args-rev) #t)
(let ((kw (car rest)))
(case kw
((#:method)
@@ -618,5 +564,3 @@
(title "Not Found"))
(body
(p "This page does not exist on the server."))))))))))))))
-
-
diff --git a/src/scm/webid-oidc/dpop-proof.scm b/src/scm/webid-oidc/dpop-proof.scm
index 54b338b..2ccbddc 100644
--- a/src/scm/webid-oidc/dpop-proof.scm
+++ b/src/scm/webid-oidc/dpop-proof.scm
@@ -20,6 +20,7 @@
#:use-module (webid-oidc jwk)
#:use-module (webid-oidc jti)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (ice-9 optargs)
#:use-module (srfi srfi-19))
@@ -186,51 +187,51 @@
(uri-path b))))
(raise-dpop-uri-mismatch a b)))
-(define*-public (dpop-proof-decode current-time jti-list method uri str cnf/check
+(define*-public (dpop-proof-decode method uri str cnf/check
#:key
(access-token #f))
- (when (date? current-time)
- (set! current-time (date->time-utc current-time)))
- (when (time? current-time)
- (set! current-time (time-second current-time)))
- (with-exception-handler
- (lambda (error)
- (raise-cannot-decode-dpop-proof str error))
- (lambda ()
- (let ((decoded (the-dpop-proof (jws-decode str dpop-proof-jwk))))
- (unless (eq? method (dpop-proof-htm decoded))
- (raise-dpop-method-mismatch (dpop-proof-htm decoded) method))
- (uris-compatible (dpop-proof-htu decoded)
- (if (string? uri)
- (string->uri uri)
- uri))
- (let ((iat (time-second (date->time-utc (dpop-proof-iat decoded)))))
- (unless (>= current-time (- iat 5))
- (raise-dpop-signed-in-future iat current-time))
- (unless (<= current-time (+ iat 120)) ;; Valid for 2 min
- (raise-dpop-too-old iat current-time)))
- (when access-token
- (let ((h (stubs:hash 'SHA-256 access-token)))
- (unless (equal? (dpop-proof-ath decoded) h)
- (raise-exception
- (make-dpop-invalid-access-token-hash (dpop-proof-ath decoded) access-token)))))
- (if (string? cnf/check)
- (unless (equal? cnf/check (stubs:jkt (dpop-proof-jwk decoded)))
- (raise-dpop-unconfirmed-key (dpop-proof-jwk decoded) cnf/check #f))
- (with-exception-handler
- (lambda (error)
- (raise-dpop-unconfirmed-key (dpop-proof-jwk decoded) #f error))
- (lambda ()
- (unless (cnf/check (stubs:jkt (dpop-proof-jwk decoded)))
- ;; deprecated; throw an error instead!
- (error "the cnf/check function returned #f")))))
- (unless (jti-check current-time (dpop-proof-jti decoded) jti-list 120)
- (with-exception-handler
- (lambda (error)
- (raise-jti-found (dpop-proof-jti decoded) error))
- (lambda ()
- (error "the jti-check function returned #f"))))
- decoded))))
+ (let ((current-time
+ (time-second (date->time-utc ((p:current-date))))))
+ (with-exception-handler
+ (lambda (error)
+ (raise-cannot-decode-dpop-proof str error))
+ (lambda ()
+ (let ((decoded (the-dpop-proof (jws-decode str dpop-proof-jwk))))
+ (unless (eq? method (dpop-proof-htm decoded))
+ (raise-dpop-method-mismatch (dpop-proof-htm decoded) method))
+ (uris-compatible (dpop-proof-htu decoded)
+ (if (string? uri)
+ (string->uri uri)
+ uri))
+ (let ((iat (time-second (date->time-utc (dpop-proof-iat decoded)))))
+ (unless (>= current-time (- iat 5))
+ (raise-dpop-signed-in-future iat current-time))
+ (unless (<= current-time (+ iat 120)) ;; Valid for 2 min
+ (raise-dpop-too-old iat current-time)))
+ (when access-token
+ (let ((h (stubs:hash 'SHA-256 access-token)))
+ (unless (equal? (dpop-proof-ath decoded) h)
+ (raise-exception
+ (make-dpop-invalid-access-token-hash (dpop-proof-ath decoded) access-token)))))
+ (if (string? cnf/check)
+ (unless (equal? cnf/check (stubs:jkt (dpop-proof-jwk decoded)))
+ (raise-dpop-unconfirmed-key (dpop-proof-jwk decoded) cnf/check #f))
+ (with-exception-handler
+ (lambda (error)
+ (raise-dpop-unconfirmed-key (dpop-proof-jwk decoded) #f error))
+ (lambda ()
+ (unless (cnf/check (stubs:jkt (dpop-proof-jwk decoded)))
+ ;; deprecated; throw an error instead!
+ (error "the cnf/check function returned #f")))))
+ (parameterize ((p:current-date current-time))
+ ;; jti-check should use the same date.
+ (unless (jti-check (dpop-proof-jti decoded) 120)
+ (with-exception-handler
+ (lambda (error)
+ (raise-jti-found (dpop-proof-jti decoded) error))
+ (lambda ()
+ (error "the jti-check function returned #f"))))
+ decoded))))))
(define-public (dpop-proof-encode dpop-proof key)
(with-exception-handler
@@ -245,11 +246,10 @@
(alg #f)
(htm #f)
(htu #f)
- (iat #f)
(access-token #f))
(dpop-proof-encode
(make-dpop-proof (make-dpop-proof-header alg client-key)
- (make-dpop-proof-payload (stubs:random 12) htm htu iat
+ (make-dpop-proof-payload (stubs:random 12) htm htu ((p:current-date))
(and access-token
(stubs:hash 'SHA-256 access-token))))
client-key))
diff --git a/src/scm/webid-oidc/hello-world.scm b/src/scm/webid-oidc/hello-world.scm
index 8e68359..d752aae 100644
--- a/src/scm/webid-oidc/hello-world.scm
+++ b/src/scm/webid-oidc/hello-world.scm
@@ -17,7 +17,6 @@
(define-module (webid-oidc hello-world)
#:use-module (webid-oidc resource-server)
#:use-module (webid-oidc server log)
- #:use-module (webid-oidc jti)
#:use-module ((webid-oidc config) #:prefix cfg:)
#:use-module (web request)
#:use-module (web response)
@@ -112,7 +111,6 @@ Options:
(G_ "You are legally required to link to the complete corresponding source code.\n"))
(exit 1))
str))
- (jti-list (make-jti-list))
(log-file (option-ref options log-file-sym #f))
(error-file (option-ref options error-file-sym #f)))
(unless (and (string->number port-string)
diff --git a/src/scm/webid-oidc/identity-provider.scm b/src/scm/webid-oidc/identity-provider.scm
index 8bd3e5b..e22f1ef 100644
--- a/src/scm/webid-oidc/identity-provider.scm
+++ b/src/scm/webid-oidc/identity-provider.scm
@@ -57,9 +57,7 @@
jwks-uri
authorization-endpoint-uri
token-endpoint-uri
- jti-list
#:key
- (current-time current-time)
(http-get http-get))
(let ((key
(catch #t
@@ -80,11 +78,9 @@
'ES256)))
(let ((authorization-endpoint
(make-authorization-endpoint subject encrypted-password alg key 120
- #:current-time current-time
#:http-get http-get))
(token-endpoint
- (make-token-endpoint token-endpoint-uri issuer alg key 3600 jti-list
- #:current-time current-time))
+ (make-token-endpoint token-endpoint-uri issuer alg key 3600))
(openid-configuration
(make-oidc-configuration jwks-uri
authorization-endpoint-uri
diff --git a/src/scm/webid-oidc/jti.scm b/src/scm/webid-oidc/jti.scm
index 4713d7d..cf05bbb 100644
--- a/src/scm/webid-oidc/jti.scm
+++ b/src/scm/webid-oidc/jti.scm
@@ -15,36 +15,42 @@
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
(define-module (webid-oidc jti)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (ice-9 atomic)
#:use-module (ice-9 threads)
- #:use-module (srfi srfi-19))
+ #:use-module (ice-9 match)
+ #:use-module (srfi srfi-9)
+ #:use-module (srfi srfi-19)
+ #:export (jti-check))
-(define-public (make-jti-list)
+(define jti-list
(make-atomic-box '()))
-(define-public (lookup list jti)
- (if (null? list)
- #f
- (or (string=? (assq-ref (car list) 'jti) jti)
- (lookup (cdr list) jti))))
-
-(define-public (jti-check current-time jti list valid-time)
- (when (date? current-time)
- (set! current-time (date->time-utc current-time)))
- (when (time? current-time)
- (set! current-time (time-second current-time)))
- (let* ((old (atomic-box-ref list))
- (new-entry `((exp . ,(+ current-time valid-time))
- (jti . ,jti)))
+(define-record-type <jti-item>
+ (make-jti-item exp jti)
+ jti-item?
+ (exp jti-item-exp)
+ (jti jti-item-jti))
+
+(define lookup
+ (match-lambda*
+ ((() item) #f)
+ (((($ <jti-item> exp jti) other ...) item)
+ (or (string=? jti item)
+ (lookup other item)))))
+
+(define (jti-check jti valid-time)
+ (let* ((current-time
+ (time-second (date->time-utc ((p:current-date)))))
+ (old (atomic-box-ref jti-list))
+ (new-entry (make-jti-item (+ current-time valid-time) jti))
(new (filter
- (lambda (entry)
- (let ((exp (assq-ref entry 'exp)))
- (>= exp current-time)))
+ (match-lambda
+ (($ <jti-item> exp other-jti)
+ (>= exp current-time)))
(cons new-entry old))))
- (let ((present? (lookup old jti)))
- (if present?
- #f
- (let ((discarded (atomic-box-compare-and-swap! list old new)))
- (if (eq? discarded old)
- #t
- (jti-check current-time jti list valid-time)))))))
+ (and (not (lookup old jti))
+ (let ((discarded (atomic-box-compare-and-swap! jti-list old new)))
+ (if (eq? discarded old)
+ #t
+ (jti-check jti valid-time))))))
diff --git a/src/scm/webid-oidc/oidc-id-token.scm b/src/scm/webid-oidc/oidc-id-token.scm
index 9fe276c..e95efaf 100644
--- a/src/scm/webid-oidc/oidc-id-token.scm
+++ b/src/scm/webid-oidc/oidc-id-token.scm
@@ -20,6 +20,7 @@
#:use-module (webid-oidc jws)
#:use-module (webid-oidc jti)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (web client)
#:use-module (ice-9 optargs)
@@ -206,12 +207,13 @@
(iss #f)
(sub #f)
(aud #f)
- (exp #f)
- (iat #f))
+ (validity 3600))
(unless sub
(set! sub webid))
(id-token-encode
(make-id-token
`((alg . ,(symbol->string alg)))
- (make-id-token-payload webid iss sub aud (stubs:random 12) exp iat))
+ (let ((iat (time-second (date->time-utc ((p:current-date))))))
+ (make-id-token-payload webid iss sub aud (stubs:random 12)
+ (+ iat validity) iat)))
issuer-key))
diff --git a/src/scm/webid-oidc/parameters.scm b/src/scm/webid-oidc/parameters.scm
new file mode 100644
index 0000000..3b24361
--- /dev/null
+++ b/src/scm/webid-oidc/parameters.scm
@@ -0,0 +1,34 @@
+(define-module (webid-oidc parameters)
+ #:use-module (srfi srfi-19)
+ #:use-module (webid-oidc jti)
+ #:export (data-home cache-home current-date))
+
+(define data-home
+ (make-parameter
+ (format #f "~a/disfluid"
+ (or (getenv "XDG_DATA_HOME")
+ (format #f "~a/.local/share"
+ (getenv "HOME"))))))
+
+(define cache-home
+ (make-parameter
+ (format #f "~a/disfluid"
+ (or (getenv "XDG_CACHE_HOME")
+ (format #f "~a/.cache"
+ (getenv "HOME"))))))
+
+(define current-date
+ ;; This parameter is a thunk!
+ (make-parameter
+ (lambda ()
+ (time-utc->date (current-time)))
+ (lambda (thunk)
+ (lambda ()
+ (let ((date (if (thunk? thunk)
+ (thunk)
+ thunk)))
+ (when (integer? date)
+ (set! date (make-time time-utc 0 date)))
+ (when (time? date)
+ (set! date (time-utc->date date)))
+ date)))))
diff --git a/src/scm/webid-oidc/program.scm b/src/scm/webid-oidc/program.scm
index 2eda34c..9d65b70 100644
--- a/src/scm/webid-oidc/program.scm
+++ b/src/scm/webid-oidc/program.scm
@@ -25,6 +25,7 @@
#:use-module (webid-oidc jti)
#:use-module (webid-oidc offloading)
#:use-module (webid-oidc catalog)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module ((webid-oidc config) #:prefix cfg:)
#:use-module (ice-9 optargs)
@@ -103,81 +104,95 @@
(prepare-log-file log-file))
(when error-file
(prepare-error-file error-file))
- (call/ec
- (lambda (return)
- (with-exception-handler
- (lambda (error)
- (with-mutex logging-mutex
- (format (current-error-port)
- (G_ "~a: ~a: Internal server error: ~a\n")
- (date->string (time-utc->date (current-time)))
- (request-ip-address request)
- (error->str error)))
- (return
- (build-response #:code 500
- #:reason-phrase "Internal Server Error"
- #:headers `((source . ,complete-corresponding-source)))
- "Sorry, there was an error."))
- (lambda ()
- (with-exception-handler
- (lambda (error)
- (with-mutex logging-mutex
- (format (current-error-port)
- (G_ "The client locale ~s can’t be approximated by system locale ~s (because ~a), using C.\n")
- ((record-accessor &unknown-client-locale 'web-locale) error)
- ((record-accessor &unknown-client-locale 'c-locale) error)
- (error->str error))))
- (lambda ()
- (receive (response response-body user cause)
- (call-with-values
- (lambda ()
- (handler request request-body))
- (case-lambda
- ((response response-body)
- (values response response-body #f #f))
- ((response response-body user)
- (values response response-body user #f))
- ((response response-body user cause)
- (values response response-body user cause))))
- (let ((logging-port
- (let ((response-code (response-code response)))
- (if (>= response-code 400)
- ;; That’s an error
- (current-error-port)
- (current-output-port)))))
+ (parameterize ((p:data-home
+ (string-append
+ (or (getenv "XDG_DATA_HOME")
+ (string-append (getenv "HOME") "/.local/share"))
+ "/disfluid"))
+ (p:cache-home
+ (string-append
+ (or (getenv "XDG_CACHE_HOME")
+ (string-append (getenv "HOME") "/.cache"))
+ "/disfluid"))
+ ;; Fix the date
+ (p:current-date ((p:current-date))))
+ (call/ec
+ (lambda (return)
+ (with-exception-handler
+ (lambda (error)
+ (with-mutex logging-mutex
+ (format (current-error-port)
+ (G_ "~a: ~a: Internal server error: ~a\n")
+ (date->string ((p:current-date)))
+ (request-ip-address request)
+ (error->str error)))
+ (return
+ (build-response #:code 500
+ #:reason-phrase "Internal Server Error"
+ #:headers `((source . ,complete-corresponding-source)
+ (date . ,((p:current-date)))))
+ "Sorry, there was an error."))
+ (lambda ()
+ (with-exception-handler
+ (lambda (error)
(with-mutex logging-mutex
- (format logging-port
- (G_ "~a: ~s ~a ~s ~a\n")
- (if user
- (format #f (G_ "~a: ~a (~a)")
- (date->string (time-utc->date (current-time)))
- (uri->string user)
- (request-ip-address request))
- (format #f (G_ "~a: ~a")
- (date->string (time-utc->date (current-time)))
- (request-ip-address request)))
- (request-method request)
- (uri-path (request-uri request))
- (response-code response)
- (if cause
- (string-append
- (response-reason-phrase response)
- " "
- (format #f (G_ "(there was an error: ~a)")
- (error->str cause)))
- (response-reason-phrase response)))))
- (return
- (build-response
- #:version (response-version response)
- #:code (response-code response)
- #:reason-phrase (response-reason-phrase response)
- #:headers (cons `(source . ,complete-corresponding-source)
- (response-headers response))
- #:port (response-port response)
- #:validate-headers? #t)
- response-body)))
- #:unwind? #t
- #:unwind-for-type &unknown-client-locale)))))))
+ (format (current-error-port)
+ (G_ "The client locale ~s can’t be approximated by system locale ~s (because ~a), using C.\n")
+ ((record-accessor &unknown-client-locale 'web-locale) error)
+ ((record-accessor &unknown-client-locale 'c-locale) error)
+ (error->str error))))
+ (lambda ()
+ (receive (response response-body user cause)
+ (call-with-values
+ (lambda ()
+ (handler request request-body))
+ (case-lambda
+ ((response response-body)
+ (values response response-body #f #f))
+ ((response response-body user)
+ (values response response-body user #f))
+ ((response response-body user cause)
+ (values response response-body user cause))))
+ (let ((logging-port
+ (let ((response-code (response-code response)))
+ (if (>= response-code 400)
+ ;; That’s an error
+ (current-error-port)
+ (current-output-port)))))
+ (with-mutex logging-mutex
+ (format logging-port
+ (G_ "~a: ~s ~a ~s ~a\n")
+ (if user
+ (format #f (G_ "~a: ~a (~a)")
+ (date->string (time-utc->date (current-time)))
+ (uri->string user)
+ (request-ip-address request))
+ (format #f (G_ "~a: ~a")
+ (date->string (time-utc->date (current-time)))
+ (request-ip-address request)))
+ (request-method request)
+ (uri-path (request-uri request))
+ (response-code response)
+ (if cause
+ (string-append
+ (response-reason-phrase response)
+ " "
+ (format #f (G_ "(there was an error: ~a)")
+ (error->str cause)))
+ (response-reason-phrase response)))))
+ (return
+ (build-response
+ #:version (response-version response)
+ #:code (response-code response)
+ #:reason-phrase (response-reason-phrase response)
+ #:headers `((source . ,complete-corresponding-source)
+ (date . ,((p:current-date)))
+ ,@(response-headers response))
+ #:port (response-port response)
+ #:validate-headers? #t)
+ response-body)))
+ #:unwind? #t
+ #:unwind-for-type &unknown-client-locale))))))))
(define (serve-one-client* handler implementation server state)
;; Same as serve-one-client, except it is served in a promise.
@@ -753,8 +768,6 @@ Rreleased ~a\n")
(make-identity-provider
server-name key-file subject encrypted-password jwks-uri
authorization-endpoint-uri token-endpoint-uri
- (make-jti-list)
- #:current-time current-time
#:http-get cache-http-get)))
(run-server*
(handler-with-log
@@ -821,49 +834,45 @@ Rreleased ~a\n")
(format (current-error-port) (G_ "You must pass --~a to set the token endpoint URI.\n")
token-endpoint-uri-sym)
(exit 1))
- (let ((jti-list (make-jti-list)))
- (let ((resource-handler
- (make-resource-server
- #:server-uri server-name
- #:owner subject
- #:authenticator
- (if header
- (begin
- (set! header
- (string->symbol
- (string-downcase
- (symbol->string header))))
- (lambda (request request-body)
- (let ((value (assq-ref (request-headers request) header)))
- (and value (string->uri value)))))
- (make-authenticator
- jti-list
- #:server-uri server-name
- #:http-get cache-http-get))
- #:http-get cache-http-get))
- (identity-provider-handler
- (make-identity-provider
- server-name key-file subject encrypted-password jwks-uri
- authorization-endpoint-uri token-endpoint-uri
- jti-list
- #:current-time current-time
- #:http-get cache-http-get)))
- (create-root server-name subject)
- (run-server*
- (handler-with-log
- (option-ref options log-file-sym #f)
- (option-ref options error-file-sym #f)
- complete-corresponding-source
- (lambda (request request-body)
- (let ((path (uri-path (request-uri request))))
- (if (or (equal? path "/.well-known/openid-configuration")
- (equal? path (uri-path jwks-uri))
- (equal? path (uri-path authorization-endpoint-uri))
- (equal? path (uri-path token-endpoint-uri)))
- (identity-provider-handler request request-body)
- (resource-handler request request-body)))))
- 'http
- (list #:port port)))))
+ (let ((resource-handler
+ (make-resource-server
+ #:server-uri server-name
+ #:owner subject
+ #:authenticator
+ (if header
+ (begin
+ (set! header
+ (string->symbol
+ (string-downcase
+ (symbol->string header))))
+ (lambda (request request-body)
+ (let ((value (assq-ref (request-headers request) header)))
+ (and value (string->uri value)))))
+ (make-authenticator
+ #:server-uri server-name
+ #:http-get cache-http-get))
+ #:http-get cache-http-get))
+ (identity-provider-handler
+ (make-identity-provider
+ server-name key-file subject encrypted-password jwks-uri
+ authorization-endpoint-uri token-endpoint-uri
+ #:http-get cache-http-get)))
+ (create-root server-name subject)
+ (run-server*
+ (handler-with-log
+ (option-ref options log-file-sym #f)
+ (option-ref options error-file-sym #f)
+ complete-corresponding-source
+ (lambda (request request-body)
+ (let ((path (uri-path (request-uri request))))
+ (if (or (equal? path "/.well-known/openid-configuration")
+ (equal? path (uri-path jwks-uri))
+ (equal? path (uri-path authorization-endpoint-uri))
+ (equal? path (uri-path token-endpoint-uri)))
+ (identity-provider-handler request request-body)
+ (resource-handler request request-body)))))
+ 'http
+ (list #:port port))))
(else
(format (current-error-port) (G_ "Unknown command ~s\n")
command)
diff --git a/src/scm/webid-oidc/refresh-token.scm b/src/scm/webid-oidc/refresh-token.scm
index 34b2f1b..e3fbf7c 100644
--- a/src/scm/webid-oidc/refresh-token.scm
+++ b/src/scm/webid-oidc/refresh-token.scm
@@ -18,36 +18,33 @@
#:use-module (webid-oidc errors)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module (webid-oidc jwk)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (ice-9 optargs)
#:use-module (ice-9 threads)
- #:use-module (srfi srfi-19))
+ #:use-module (srfi srfi-19)
+ #:export
+ (
+ list-refresh-tokens
+ update-refresh-token-list
+ issue-refresh-token
+ with-refresh-token
+ remove-refresh-token
+ ))
-(define-public (default-dir)
- (let ((xdg-data-home (or
- (getenv "XDG_DATA_HOME")
- (format #f "~a/.local/share"
- (getenv "HOME")))))
- (format #f "~a/disfluid" xdg-data-home)))
-
-(define*-public (list-refresh-tokens
- #:key
- (dir default-dir))
- (when (thunk? dir)
- (set! dir (dir)))
+(define (list-refresh-tokens)
(catch #t
(lambda ()
- (with-input-from-file (format #f "~a/refresh-tokens.scm" dir)
+ (with-input-from-file (format #f "~a/refresh-tokens.scm" (p:data-home))
read))
(lambda errors
'())))
+;; TODO: use stubs:atomically-update-file and remove that mutex.
(define mutex (make-mutex))
-(define* (set-refresh-token-list list
- #:key (dir default-dir))
- (when (thunk? dir)
- (set! dir (dir)))
+(define (set-refresh-token-list list)
+ (define dir (p:data-home))
(define old-file (format #f "~a/refresh-tokens.scm" dir))
(define new-file (format #f "~a/refresh-tokens.scm~" dir))
(stubs:call-with-output-file*
@@ -57,12 +54,11 @@
(close-port port)))
(rename-file new-file old-file))
-(define*-public (update-refresh-token-list f
- #:key (dir default-dir))
+(define (update-refresh-token-list f)
(with-mutex mutex
- (let ((old (list-refresh-tokens #:dir dir)))
+ (let ((old (list-refresh-tokens)))
(let ((new (f old)))
- (set-refresh-token-list new #:dir dir)))))
+ (set-refresh-token-list new)))))
(define (remove sub aud)
(lambda (old)
@@ -90,20 +86,13 @@
(refresh_token . ,jti))
(remover old)))))
-(define*-public (issue-refresh-token sub aud jkt
- #:key
- (dir default-dir))
+(define (issue-refresh-token sub aud jkt)
(define jti (stubs:random 12))
- (update-refresh-token-list (insert sub aud jkt jti)
- #:dir dir)
+ (update-refresh-token-list (insert sub aud jkt jti))
jti)
-(define*-public (with-refresh-token refresh-token
- key
- f
- #:key
- (dir default-dir))
- (let ((list (list-refresh-tokens #:dir dir)))
+(define (with-refresh-token refresh-token key f)
+ (let ((list (list-refresh-tokens)))
(define (check list)
(if (null? list)
(raise-invalid-refresh-token refresh-token)
@@ -121,7 +110,5 @@
(check tl))))))
(check list)))
-(define*-public (remove-refresh-token sub aud
- #:key
- (dir default-dir))
- (update-refresh-token-list (remove sub aud) #:dir dir))
+(define (remove-refresh-token sub aud)
+ (update-refresh-token-list (remove sub aud)))
diff --git a/src/scm/webid-oidc/resource-server.scm b/src/scm/webid-oidc/resource-server.scm
index a6c111e..14d8b81 100644
--- a/src/scm/webid-oidc/resource-server.scm
+++ b/src/scm/webid-oidc/resource-server.scm
@@ -21,12 +21,13 @@
#:use-module (webid-oidc jwk)
#:use-module (webid-oidc dpop-proof)
#:use-module (webid-oidc serve)
- #:use-module (webid-oidc server create)
- #:use-module (webid-oidc server read)
- #:use-module (webid-oidc server update)
- #:use-module (webid-oidc server delete)
+ #:use-module ((webid-oidc server create) #:prefix ldp:)
+ #:use-module ((webid-oidc server read) #:prefix ldp:)
+ #:use-module ((webid-oidc server update) #:prefix ldp:)
+ #:use-module ((webid-oidc server delete) #:prefix ldp:)
#:use-module (webid-oidc server precondition)
#:use-module (webid-oidc http-link)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module ((webid-oidc config) #:prefix cfg:)
#:use-module (webid-oidc jti)
#:use-module (webid-oidc access-token)
@@ -51,8 +52,7 @@
(car (reverse (string-split text #\|)))
out)))
-(define*-public (make-authenticator jti-list
- #:key
+(define*-public (make-authenticator #:key
(server-uri #f)
(current-time current-time)
(http-get http-get))
@@ -62,52 +62,45 @@
(let ((headers (request-headers request))
(uri (request-uri request))
(method (request-method request))
- (current-time
- (let ((t current-time))
- (when (thunk? t)
- (set! t (t)))
- (when (integer? t)
- (set! t (make-time time-utc 0 t)))
- (when (time? t)
- (set! t (time-utc->date t)))
- t)))
- (let ((authz (assoc-ref headers 'authorization))
- (dpop (assoc-ref headers 'dpop))
- (full-uri (build-uri (uri-scheme server-uri)
- #:userinfo (uri-userinfo server-uri)
- #:host (uri-host server-uri)
- #:port (uri-port server-uri)
- #:path (string-append
- "/"
- (encode-and-join-uri-path
- (append
- (split-and-decode-uri-path (uri-path server-uri))
- (split-and-decode-uri-path
- (uri-path uri))))))))
- (and authz dpop
- (eq? (car authz) 'dpop)
- (with-exception-handler
- (lambda (error)
- (format (current-error-port)
- (G_ "~a: authentication failure: ~a\n")
- (date->string current-time)
- (error->str error))
- #f)
- (lambda ()
- (let* ((lit-access-token (symbol->string (cadr authz)))
- (access-token
- (access-token-decode lit-access-token
- #:http-get http-get))
- (cnf/jkt (access-token-cnf/jkt access-token))
- (dpop-proof
- (dpop-proof-decode
- current-time jti-list method full-uri
- dpop cnf/jkt #:access-token lit-access-token)))
- (let ((subject (access-token-webid access-token))
- (issuer (access-token-iss access-token)))
- (confirm-provider subject issuer #:http-get http-get)
- subject)))
- #:unwind? #t))))))
+ (current-time ((p:current-date))))
+ (parameterize ((p:current-date current-time)) ;; fix the date
+ (let ((authz (assoc-ref headers 'authorization))
+ (dpop (assoc-ref headers 'dpop))
+ (full-uri (build-uri (uri-scheme server-uri)
+ #:userinfo (uri-userinfo server-uri)
+ #:host (uri-host server-uri)
+ #:port (uri-port server-uri)
+ #:path (string-append
+ "/"
+ (encode-and-join-uri-path
+ (append
+ (split-and-decode-uri-path (uri-path server-uri))
+ (split-and-decode-uri-path
+ (uri-path uri))))))))
+ (and authz dpop
+ (eq? (car authz) 'dpop)
+ (with-exception-handler
+ (lambda (error)
+ (format (current-error-port)
+ (G_ "~a: authentication failure: ~a\n")
+ (date->string current-time)
+ (error->str error))
+ #f)
+ (lambda ()
+ (let* ((lit-access-token (symbol->string (cadr authz)))
+ (access-token
+ (access-token-decode lit-access-token
+ #:http-get http-get))
+ (cnf/jkt (access-token-cnf/jkt access-token))
+ (dpop-proof
+ (dpop-proof-decode
+ method full-uri
+ dpop cnf/jkt #:access-token lit-access-token)))
+ (let ((subject (access-token-webid access-token))
+ (issuer (access-token-iss access-token)))
+ (confirm-provider subject issuer #:http-get http-get)
+ subject)))
+ #:unwind? #t)))))))
(define (handle-errors f g)
(call/ec
@@ -175,171 +168,170 @@
(server-uri #f)
(owner #f)
(authenticator #f)
- (current-time current-time)
(http-get http-get))
(unless owner
(error "The owner is not defined."))
(declare-link-header!)
(unless authenticator
(set! authenticator
- (make-authenticator (make-jti-list)
- #:server-uri server-uri
- #:current-time current-time
- #:http-get http-get)))
+ (make-authenticator
+ #:server-uri server-uri
+ #:http-get http-get)))
(lambda (request request-body)
- (let ((user (authenticator request request-body)))
- (handle-errors
- (lambda (return)
- (let ((method (request-method request)))
- (case method
- ((GET HEAD OPTIONS)
- (receive (headers content)
- (read server-uri owner user
- (uri-path (request-uri request))
- #:http-get http-get)
- (let ((true-content-type
- (car (assq-ref headers 'content-type)))
- (other-headers
- (filter
- (lambda (h)
- (not (eq? (car h) 'content-type)))
- headers)))
- (receive (negociated-content-type
- negociated-content)
- (convert (request-accept request #f)
- server-uri
- (uri-path (request-uri request))
- true-content-type
- content)
- (serve-get
- return
- (uri-path (request-uri request))
- (request-if-match request)
- (request-if-none-match request)
- negociated-content-type
- negociated-content
- (car (assq-ref headers 'etag))
- (cons `(content-type ,negociated-content-type)
- other-headers)
- user)))))
- ((PUT)
- (receive (content-type content)
- (nonrdf-or-turtle server-uri request request-body)
- (return
- (build-response
- #:headers
- `((etag . (,(update server-uri owner user
- (uri-path (request-uri request))
- (request-if-match request)
- (request-if-none-match request)
- content-type
- content
- #:http-get http-get)
- . #f))))
- ""
- user)))
- ((POST)
- (receive (content-type content)
- (nonrdf-or-turtle server-uri request request-body)
- (let ((types
- (map car
- (filter
- (lambda (link)
- (equal? (assq-ref link 'rel) "type"))
- (request-links request)))))
+ (parameterize ((p:current-date ((p:current-date)))) ;; Fix the date
+ (let ((user (authenticator request request-body)))
+ (handle-errors
+ (lambda (return)
+ (let ((method (request-method request)))
+ (case method
+ ((GET HEAD OPTIONS)
+ (receive (headers content)
+ (ldp:read server-uri owner user
+ (uri-path (request-uri request))
+ #:http-get http-get)
+ (let ((true-content-type
+ (car (assq-ref headers 'content-type)))
+ (other-headers
+ (filter
+ (lambda (h)
+ (not (eq? (car h) 'content-type)))
+ headers)))
+ (receive (negociated-content-type
+ negociated-content)
+ (convert (request-accept request #f)
+ server-uri
+ (uri-path (request-uri request))
+ true-content-type
+ content)
+ (serve-get
+ return
+ (uri-path (request-uri request))
+ (request-if-match request)
+ (request-if-none-match request)
+ negociated-content-type
+ negociated-content
+ (car (assq-ref headers 'etag))
+ (cons `(content-type ,negociated-content-type)
+ other-headers)
+ user)))))
+ ((PUT)
+ (receive (content-type content)
+ (nonrdf-or-turtle server-uri request request-body)
(return
(build-response
- #:code 201 #:reason-phrase "Created"
#:headers
- `((location . ,(create server-uri owner user
- (uri-path (request-uri request))
- types
- (assq-ref (request-headers request) 'slug)
- content-type
- content
- #:http-get http-get))))
+ `((etag . (,(ldp:update server-uri owner user
+ (uri-path (request-uri request))
+ (request-if-match request)
+ (request-if-none-match request)
+ content-type
+ content
+ #:http-get http-get)
+ . #f))))
""
- user))))
- ((DELETE)
- (delete server-uri owner user
- (uri-path (request-uri request))
- (request-if-match request)
- (request-if-none-match request)
- #:http-get http-get)
- (return
- (build-response)
- ""
- user)))))
- (lambda (return error)
- (if (cannot-fetch-group? error)
- (format (current-error-port) (G_ "Warning: ~a\n")
- (error->str error))
- (cond
- ((uri-slash-semantics-error? error)
- (return
- (build-response
- #:code 301
- #:reason-phrase "Found"
- #:headers
- `((location
- . ,(build-uri
- (uri-scheme server-uri)
- #:userinfo (uri-userinfo server-uri)
- #:host (uri-host server-uri)
- #:port (uri-port server-uri)
- #:path (uri-slash-semantics-error-expected-path error)))))
- #f
- user))
- ((or (path-not-found? error)
- (auxiliary-resource-absent? error)
- (forbidden? error))
- (if user
- ;; That’s a forbidden
- (return
- (build-response #:code 403 #:reason-phrase "Forbidden")
- #f
- user)
- (return
- (build-response #:code 401 #:reason-phrase "Unauthorized"
- #:headers `((www-authenticate . ((DPoP)))))
- #f
- user)))
- ((or (cannot-delete-root? error))
- (return
- (build-response
- #:code 405
- #:reason-phrase "Method Not Allowed")
- #f
- user))
- ((or (container-not-empty? error)
- (incorrect-containment-triples? error)
- (path-is-auxiliary? error))
- (return
- (build-response
- #:code 409
- #:reason-phrase "Conflict")
- #f
- user))
- ((unsupported-media-type? error)
- (return
- (build-response
- #:code 415
- #:reason-phrase "Unsupported Media Type")
- #f
- user))
- ((precondition-failed? error)
- (return
- (build-response
- #:code 412
- #:reason-phrase "Precondition Failed")
- #f
- user))
- ((not-acceptable? error)
- (return
- (build-response
- #:code 406
- #:reason-phrase "Not Acceptable")
- #f
- user))
- (else
- (raise-exception error)))))))))
+ user)))
+ ((POST)
+ (receive (content-type content)
+ (nonrdf-or-turtle server-uri request request-body)
+ (let ((types
+ (map car
+ (filter
+ (lambda (link)
+ (equal? (assq-ref link 'rel) "type"))
+ (request-links request)))))
+ (return
+ (build-response
+ #:code 201 #:reason-phrase "Created"
+ #:headers
+ `((location . ,(ldp:create server-uri owner user
+ (uri-path (request-uri request))
+ types
+ (assq-ref (request-headers request) 'slug)
+ content-type
+ content
+ #:http-get http-get))))
+ ""
+ user))))
+ ((DELETE)
+ (ldp:delete server-uri owner user
+ (uri-path (request-uri request))
+ (request-if-match request)
+ (request-if-none-match request)
+ #:http-get http-get)
+ (return
+ (build-response)
+ ""
+ user)))))
+ (lambda (return error)
+ (if (cannot-fetch-group? error)
+ (format (current-error-port) (G_ "Warning: ~a\n")
+ (error->str error))
+ (cond
+ ((uri-slash-semantics-error? error)
+ (return
+ (build-response
+ #:code 301
+ #:reason-phrase "Found"
+ #:headers
+ `((location
+ . ,(build-uri
+ (uri-scheme server-uri)
+ #:userinfo (uri-userinfo server-uri)
+ #:host (uri-host server-uri)
+ #:port (uri-port server-uri)
+ #:path (uri-slash-semantics-error-expected-path error)))))
+ #f
+ user))
+ ((or (path-not-found? error)
+ (auxiliary-resource-absent? error)
+ (forbidden? error))
+ (if user
+ ;; That’s a forbidden
+ (return
+ (build-response #:code 403 #:reason-phrase "Forbidden")
+ #f
+ user)
+ (return
+ (build-response #:code 401 #:reason-phrase "Unauthorized"
+ #:headers `((www-authenticate . ((DPoP)))))
+ #f
+ user)))
+ ((or (cannot-delete-root? error))
+ (return
+ (build-response
+ #:code 405
+ #:reason-phrase "Method Not Allowed")
+ #f
+ user))
+ ((or (container-not-empty? error)
+ (incorrect-containment-triples? error)
+ (path-is-auxiliary? error))
+ (return
+ (build-response
+ #:code 409
+ #:reason-phrase "Conflict")
+ #f
+ user))
+ ((unsupported-media-type? error)
+ (return
+ (build-response
+ #:code 415
+ #:reason-phrase "Unsupported Media Type")
+ #f
+ user))
+ ((precondition-failed? error)
+ (return
+ (build-response
+ #:code 412
+ #:reason-phrase "Precondition Failed")
+ #f
+ user))
+ ((not-acceptable? error)
+ (return
+ (build-response
+ #:code 406
+ #:reason-phrase "Not Acceptable")
+ #f
+ user))
+ (else
+ (raise-exception error))))))))))
diff --git a/src/scm/webid-oidc/reverse-proxy.scm b/src/scm/webid-oidc/reverse-proxy.scm
index f9caba6..a1b05e3 100644
--- a/src/scm/webid-oidc/reverse-proxy.scm
+++ b/src/scm/webid-oidc/reverse-proxy.scm
@@ -18,8 +18,8 @@
#:use-module (webid-oidc errors)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module (webid-oidc resource-server)
- #:use-module (webid-oidc jti)
#:use-module ((webid-oidc config) #:prefix cfg:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (ice-9 optargs)
#:use-module (ice-9 receive)
#:use-module (ice-9 i18n)
@@ -36,9 +36,7 @@
(define*-public (make-reverse-proxy
#:key
- (jti-list #f)
(server-uri #f)
- (current-time current-time)
(http-get http-get)
(endpoint #f)
(auth-header 'XXX-Agent))
@@ -50,9 +48,7 @@
(symbol->string auth-header))))
(define authenticate
(make-authenticator
- (or jti-list (make-jti-list))
#:server-uri server-uri
- #:current-time current-time
#:http-get http-get))
(unless (and endpoint (uri? endpoint))
(error "#:endpoint argument is not present or not an URI."))
@@ -68,43 +64,46 @@
unconfirmed-issuer)
#f)
(else
- (apply throw key args)))))))
- (let ((raw-headers (request-headers request)))
- (let ((modified-headers
- (append
- (if agent
- (list (cons auth-header (uri->string agent)))
- '())
- (filter
- (lambda (h)
- (not (eq? (car h) auth-header)))
- raw-headers))))
- (let ((modified-request
- (build-request
- (request-uri request)
- #:method (request-method request)
- #:headers modified-headers)))
- (let ((port (open-socket-for-uri endpoint)))
- (let ((request-with-port
- (write-request modified-request port)))
- (when request-body
- (unless (bytevector? request-body)
- (set! request-body (string->utf8 request-body)))
- (write-request-body request-with-port request-body))
- (force-output (request-port request-with-port))
- (let ((response (read-response port)))
- (let ((response-body
- (or (response-must-not-include-body? response)
- (read-response-body response))))
- (let ((adapted-response
- (build-response
- #:code (response-code response)
- #:reason-phrase (response-reason-phrase response)
- #:headers
- (append
- (if (eqv? (response-code response) 401)
- (list (cons 'www-authenticate '((DPoP))))
- '())
- (response-headers response)))))
- (close-port port)
- (values adapted-response response-body))))))))))))
+ (apply throw key args))))))
+ (request-time ((p:current-date))))
+ (parameterize ((p:current-date request-time))
+ ;; The time is now set for the duration of the request
+ (let ((raw-headers (request-headers request)))
+ (let ((modified-headers
+ (append
+ (if agent
+ (list (cons auth-header (uri->string agent)))
+ '())
+ (filter
+ (lambda (h)
+ (not (eq? (car h) auth-header)))
+ raw-headers))))
+ (let ((modified-request
+ (build-request
+ (request-uri request)
+ #:method (request-method request)
+ #:headers modified-headers)))
+ (let ((port (open-socket-for-uri endpoint)))
+ (let ((request-with-port
+ (write-request modified-request port)))
+ (when request-body
+ (unless (bytevector? request-body)
+ (set! request-body (string->utf8 request-body)))
+ (write-request-body request-with-port request-body))
+ (force-output (request-port request-with-port))
+ (let ((response (read-response port)))
+ (let ((response-body
+ (or (response-must-not-include-body? response)
+ (read-response-body response))))
+ (let ((adapted-response
+ (build-response
+ #:code (response-code response)
+ #:reason-phrase (response-reason-phrase response)
+ #:headers
+ (append
+ (if (eqv? (response-code response) 401)
+ (list (cons 'www-authenticate '((DPoP))))
+ '())
+ (response-headers response)))))
+ (close-port port)
+ (values adapted-response response-body)))))))))))))
diff --git a/src/scm/webid-oidc/server/resource/content.scm b/src/scm/webid-oidc/server/resource/content.scm
index 29d8889..57c51dd 100644
--- a/src/scm/webid-oidc/server/resource/content.scm
+++ b/src/scm/webid-oidc/server/resource/content.scm
@@ -19,6 +19,7 @@
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module (webid-oidc rdf-index)
#:use-module ((webid-oidc refresh-token) #:prefix refresh:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (rnrs bytevectors)
#:use-module (ice-9 exceptions)
@@ -36,18 +37,18 @@
))
-(define (default-dir)
- (string-append (refresh:default-dir) "/server"))
-
(define-class <content> ()
(content-type #:init-keyword #:content-type #:getter content-type)
(contained #:init-keyword #:contained #:getter contained)
(static-content #:init-keyword #:static-content #:getter static-content))
-(define (load-content session dir etag)
+(define (load-content session etag)
(let ((first-char (substring etag 0 1))
(rest (substring etag 1)))
- (call-with-input-file (format #f "~a/content/~a/~a" dir first-char rest)
+ (call-with-input-file (format #f "~a/server/content/~a/~a"
+ (p:data-home)
+ first-char
+ rest)
(lambda (port)
(let ((properties (read port)))
(set-port-encoding! port "ISO-8859-1")
@@ -60,14 +61,14 @@
(hash-set! session etag ret)
ret))))))
-(define (new-content session dir content-type contained static-content)
+(define (new-content session content-type contained static-content)
(when (string? static-content)
(set! static-content (string->utf8 static-content)))
(let ((etag (stubs:random 12)))
(let ((first-char (substring etag 0 1))
(rest (substring etag 1)))
- (stubs:mkdir-p (format #f "~a/content/~a" dir first-char))
- (let ((port (open (format #f "~a/content/~a/~a" dir first-char rest)
+ (stubs:mkdir-p (format #f "~a/server/content/~a" (p:data-home) first-char))
+ (let ((port (open (format #f "~a/server/content/~a/~a" (p:data-home) first-char rest)
(logior O_WRONLY O_CREAT O_EXCL))))
(write `((content-type . ,content-type)
(contained . ,contained)) port)
@@ -82,18 +83,16 @@
#:static-content static-content))
etag))))
-(define (delete-content dir etag)
+(define (delete-content etag)
(let ((first-char (substring etag 0 1))
(rest (substring etag 1)))
- (delete-file (format #f "~a/content/~a/~a" dir first-char rest))))
+ (delete-file (format #f "~a/server/content/~a/~a" (p:data-home) first-char rest))))
-(define* (with-session f #:key (dir default-dir))
- (when (thunk? dir)
- (set! dir (dir)))
+(define (with-session f)
(let ((session (make-hash-table)))
(define (do-load etag)
(or (hash-ref session etag)
- (load-content session dir etag)))
+ (load-content session etag)))
(define (get-content-type etag)
(content-type (do-load etag)))
(define (get-contained etag)
@@ -101,7 +100,7 @@
(define (get-static-content etag)
(static-content (do-load etag)))
(define (do-create content-type contained static-content)
- (new-content session dir content-type contained static-content))
+ (new-content session content-type contained static-content))
(define (do-delete etag)
- (delete-content dir etag))
+ (delete-content etag))
(f get-content-type get-contained get-static-content do-create do-delete)))
diff --git a/src/scm/webid-oidc/server/resource/path.scm b/src/scm/webid-oidc/server/resource/path.scm
index f1594bc..55c4274 100644
--- a/src/scm/webid-oidc/server/resource/path.scm
+++ b/src/scm/webid-oidc/server/resource/path.scm
@@ -19,6 +19,7 @@
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module (webid-oidc rdf-index)
#:use-module ((webid-oidc refresh-token) #:prefix refresh:)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module (web uri)
#:use-module (rnrs bytevectors)
#:use-module (ice-9 exceptions)
@@ -47,17 +48,14 @@
))
-(define (default-dir)
- (string-append (refresh:default-dir) "/server"))
-
(define (hash-path/lock path)
(let ((h (stubs:hash 'SHA-256 path))
- (dir (default-dir)))
+ (dir (p:data-home)))
(let ((first-char (substring h 0 1))
(rest (substring h 1)))
(values
- (format #f "~a/path/~a/~a" dir first-char rest)
- (format #f "~a/path/~a/.lock" dir first-char)))))
+ (format #f "~a/server/path/~a/~a" dir first-char rest)
+ (format #f "~a/server/path/~a/.lock" dir first-char)))))
(define (hash-path path)
(receive (h lock) (hash-path/lock path)
diff --git a/src/scm/webid-oidc/stubs.scm b/src/scm/webid-oidc/stubs.scm
index 0c6f0bc..08d15aa 100644
--- a/src/scm/webid-oidc/stubs.scm
+++ b/src/scm/webid-oidc/stubs.scm
@@ -17,6 +17,7 @@
(define-module (webid-oidc stubs)
#:use-module (webid-oidc config)
#:use-module (webid-oidc errors)
+ #:use-module (webid-oidc parameters)
#:use-module (json))
(load-extension
@@ -71,11 +72,16 @@
(lambda error
(raise-unsupported-alg (cadr error)))))
+(define (fix-random-init!)
+ (setenv "XDG_CACHE_HOME" (cache-home))
+ (setenv "DISFLUID_APPLICATION_NAME" ".")
+ (random-init!))
+
(export
base64-encode
(fix-base64-decode . base64-decode)
random
- random-init!
+ (fix-random-init! . random-init!)
(fix-generate-key . generate-key)
(fix-kty . kty)
strip-key
diff --git a/src/scm/webid-oidc/testing.scm b/src/scm/webid-oidc/testing.scm
index 0aec4b8..aec9504 100644
--- a/src/scm/webid-oidc/testing.scm
+++ b/src/scm/webid-oidc/testing.scm
@@ -16,31 +16,23 @@
(define-module (webid-oidc testing)
#:use-module (webid-oidc stubs)
- #:use-module (webid-oidc errors))
+ #:use-module (webid-oidc errors)
+ #:use-module (webid-oidc parameters))
;; This module is used only when running tests.
(define-public (with-test-environment test-name f)
- (let ((cache-dir (format #f "tests/~a.cache" test-name))
- (data-dir (format #f "tests/~a.home" test-name)))
- (setenv "XDG_CACHE_HOME" cache-dir)
- (setenv "XDG_DATA_HOME" data-dir)
- (catch #t
- (lambda () (mkdir cache-dir))
- (lambda err #t))
- (let ((pkg-cache-dir (format #f "~a/disfluid" cache-dir)))
- (catch #t
- (lambda () (mkdir pkg-cache-dir))
- (lambda err #t))
- (let ((seed-file-name (format #f "~a/seed" pkg-cache-dir)))
- (with-output-to-file seed-file-name
- (lambda ()
- (format #t "This is the initial seed for the random number generator"))))))
(with-exception-handler
(lambda (error)
(format (current-error-port) "The test failed, because ~a.\n"
(error->str error))
(raise-exception error))
(lambda ()
- (random-init!)
- (f))))
+ (parameterize ((data-home (format #f "tests/~a.home/disfluid" test-name))
+ (cache-home (format #f "tests/~a.cache/disfluid" test-name)))
+ (call-with-output-file*
+ (format #f "~a/seed" (cache-home))
+ (lambda (port)
+ (format port "This is the initial seed for the random number generator")))
+ (random-init!)
+ (f)))))
diff --git a/src/scm/webid-oidc/token-endpoint.scm b/src/scm/webid-oidc/token-endpoint.scm
index 5a05945..7c4d41c 100644
--- a/src/scm/webid-oidc/token-endpoint.scm
+++ b/src/scm/webid-oidc/token-endpoint.scm
@@ -22,6 +22,7 @@
#:use-module (webid-oidc jwk)
#:use-module (webid-oidc oidc-id-token)
#:use-module (webid-oidc access-token)
+ #:use-module ((webid-oidc parameters) #:prefix p:)
#:use-module ((webid-oidc stubs) #:prefix stubs:)
#:use-module ((webid-oidc refresh-token) #:prefix refresh:)
#:use-module (web client)
@@ -76,116 +77,101 @@
thunk
#:unwind? #t))))
-(define*-public (make-token-endpoint token-endpoint-uri iss alg jwk validity jti-list
- #:key
- (refresh-token-dir refresh:default-dir)
- (current-time current-time))
+(define*-public (make-token-endpoint token-endpoint-uri iss alg jwk validity)
(lambda* (request request-body)
(try-handle-web-failure
(lambda ()
(when (bytevector? request-body)
(set! request-body (utf8->string request-body)))
- (let ((current-time
- (let ((t current-time))
- (when (thunk? t)
- (set! t (t)))
- (when (integer? t)
- (set! t (make-time time-utc 0 t)))
- (when (time? t)
- (set! t (time-utc->date t)))
- t))
- (form-args
- (if (and (request-content-type request)
- (eq? (car (request-content-type request))
- 'application/x-www-form-urlencoded))
- (filter
- (lambda (x) x)
- (map (lambda (kv)
- (let ((parsed
- (list->vector
- (map (lambda (x)
- (uri-decode x #:decode-plus-to-space? #t))
- (string-split kv #\=)))))
- (if (eq? (vector-length parsed) 2)
- `(,(vector-ref parsed 0) . ,(vector-ref parsed 1))
- #f)))
- (string-split request-body #\&)))
- '()))
- (method (request-method request))
- ;; Maybe we’re behind a reverse proxy, so the authority of
- ;; (request-uri request) is meaningless.
- (uri (build-uri (uri-scheme token-endpoint-uri)
- #:userinfo (uri-userinfo token-endpoint-uri)
- #:host (uri-host token-endpoint-uri)
- #:port (uri-port token-endpoint-uri)
- #:path (uri-path (request-uri request))
- #:query (uri-query (request-uri request)))))
- (let ((grant-type (assoc-ref form-args "grant_type"))
- (dpop (dpop-proof-decode
- current-time jti-list method uri
- (assq-ref (request-headers request) 'dpop)
- (lambda (jkt) #t))))
- (unless (and grant-type (string? grant-type))
- (raise-unsupported-grant-type #f))
- (receive (webid client-id)
- (case (string->symbol grant-type)
- ((authorization_code)
- (let ((code
- (let ((str (assoc-ref form-args "code")))
- (unless str
- (raise-no-authorization-code))
- (authorization-code-decode
- current-time jti-list str jwk))))
- (values (authorization-code-webid code)
- (authorization-code-client-id code))))
- ((refresh_token)
- (let ((refresh-token (assoc-ref form-args "refresh_token")))
- (unless refresh-token
- (raise-no-refresh-token))
- (refresh:with-refresh-token
- refresh-token
- (dpop-proof-jwk dpop)
- values
- #:dir refresh-token-dir)))
- (else
- (raise-unsupported-grant-type grant-type)))
- (let* ((iat (time-second (date->time-utc current-time)))
- (exp (+ iat validity)))
- (let ((id-token
- (issue-id-token
- jwk
- #:alg alg
- #:webid (uri->string webid)
- #:sub (uri->string webid)
- #:iss (uri->string iss)
- #:aud (uri->string client-id)
- #:exp exp
- #:iat iat))
- (access-token
- (issue-access-token
- jwk
- #:alg alg
- #:webid (uri->string webid)
- #:iss (uri->string iss)
- #:exp exp
- #:iat iat
- #:client-key (dpop-proof-jwk dpop)
- #:client-id (uri->string client-id)))
- (refresh-token
- (if (equal? grant-type "refresh_token")
- (assoc-ref form-args "refresh_token")
- (refresh:issue-refresh-token webid client-id
- (jkt (dpop-proof-jwk dpop))
- #:dir refresh-token-dir))))
- (values
- (build-response #:headers '((content-type application/json)
- (cache-control (no-cache no-store)))
- #:port #f)
- (stubs:scm->json-string
- `((id_token . ,id-token)
- (access_token . ,access-token)
- (token_type . "DPoP")
- (expires_in . ,validity)
- (refresh_token . ,refresh-token)))
- client-id
- #f))))))))))
+ (parameterize ((p:current-date ((p:current-date))))
+ (let ((current-time ((p:current-date))) ;; thunk parameter
+ (form-args
+ (if (and (request-content-type request)
+ (eq? (car (request-content-type request))
+ 'application/x-www-form-urlencoded))
+ (filter
+ (lambda (x) x)
+ (map (lambda (kv)
+ (let ((parsed
+ (list->vector
+ (map (lambda (x)
+ (uri-decode x #:decode-plus-to-space? #t))
+ (string-split kv #\=)))))
+ (if (eq? (vector-length parsed) 2)
+ `(,(vector-ref parsed 0) . ,(vector-ref parsed 1))
+ #f)))
+ (string-split request-body #\&)))
+ '()))
+ (method (request-method request))
+ ;; Maybe we’re behind a reverse proxy, so the authority of
+ ;; (request-uri request) is meaningless.
+ (uri (build-uri (uri-scheme token-endpoint-uri)
+ #:userinfo (uri-userinfo token-endpoint-uri)
+ #:host (uri-host token-endpoint-uri)
+ #:port (uri-port token-endpoint-uri)
+ #:path (uri-path (request-uri request))
+ #:query (uri-query (request-uri request)))))
+ (let ((grant-type (assoc-ref form-args "grant_type"))
+ (dpop (dpop-proof-decode
+ method uri
+ (assq-ref (request-headers request) 'dpop)
+ (lambda (jkt) #t))))
+ (unless (and grant-type (string? grant-type))
+ (raise-unsupported-grant-type #f))
+ (receive (webid client-id)
+ (case (string->symbol grant-type)
+ ((authorization_code)
+ (let ((code
+ (let ((str (assoc-ref form-args "code")))
+ (unless str
+ (raise-no-authorization-code))
+ (authorization-code-decode str jwk))))
+ (values (authorization-code-webid code)
+ (authorization-code-client-id code))))
+ ((refresh_token)
+ (let ((refresh-token (assoc-ref form-args "refresh_token")))
+ (unless refresh-token
+ (raise-no-refresh-token))
+ (refresh:with-refresh-token
+ refresh-token
+ (dpop-proof-jwk dpop)
+ values)))
+ (else
+ (raise-unsupported-grant-type grant-type)))
+ (let* ((iat (time-second (date->time-utc current-time)))
+ (exp (+ iat validity)))
+ (let ((id-token
+ (issue-id-token
+ jwk
+ #:alg alg
+ #:webid (uri->string webid)
+ #:sub (uri->string webid)
+ #:iss (uri->string iss)
+ #:aud (uri->string client-id)
+ #:validity 3600))
+ (access-token
+ (issue-access-token
+ jwk
+ #:alg alg
+ #:webid (uri->string webid)
+ #:iss (uri->string iss)
+ #:validity 3600
+ #:client-key (dpop-proof-jwk dpop)
+ #:client-id (uri->string client-id)))
+ (refresh-token
+ (if (equal? grant-type "refresh_token")
+ (assoc-ref form-args "refresh_token")
+ (refresh:issue-refresh-token webid client-id
+ (jkt (dpop-proof-jwk dpop))))))
+ (values
+ (build-response #:headers '((content-type application/json)
+ (cache-control (no-cache no-store)))
+ #:port #f)
+ (stubs:scm->json-string
+ `((id_token . ,id-token)
+ (access_token . ,access-token)
+ (token_type . "DPoP")
+ (expires_in . ,validity)
+ (refresh_token . ,refresh-token)))
+ client-id
+ #f)))))))))))