summaryrefslogtreecommitdiff
path: root/src/scm/webid-oidc/identity-provider.scm
blob: 6f96b44557bb4e20b958cb6e084df761c5950951 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(define-module (webid-oidc identity-provider)
  #:use-module (webid-oidc errors)
  #:use-module (webid-oidc authorization-endpoint)
  #:use-module (webid-oidc token-endpoint)
  #:use-module (webid-oidc oidc-configuration)
  #:use-module (webid-oidc jwk)
  #:use-module ((webid-oidc config) #:prefix cfg:)
  #:use-module ((webid-oidc stubs) #:prefix stubs:)
  #:use-module (webid-oidc jti)
  #:use-module (web request)
  #:use-module (web response)
  #:use-module (web uri)
  #:use-module (web client)
  #:use-module (web server)
  #:use-module (webid-oidc cache)
  #:use-module (ice-9 optargs)
  #:use-module (ice-9 receive)
  #:use-module (ice-9 i18n)
  #:use-module (ice-9 getopt-long)
  #:use-module (ice-9 suspendable-ports)
  #:use-module (sxml simple)
  #:use-module (srfi srfi-19)
  #:use-module (rnrs bytevectors))

(define (G_ text)
  (let ((out (gettext text)))
    (if (string=? out text)
        ;; No translation, disambiguate
        (car (reverse (string-split text #\|)))
        out)))

(define* (same-uri? a b #:key (skip-query #f))
  (and (equal? (uri-path a) (uri-path b))
       (or skip-query (equal? (uri-query a) (uri-query b)))))

(define*-public (make-identity-provider
                 issuer
                 key-file
                 subject
                 password
                 jwks-uri
                 authorization-endpoint-uri
                 token-endpoint-uri
                 jti-list
                 #:key
                 (current-time current-time)
                 (http-get http-get))
  (let ((key
         (catch #t
           (lambda ()
             (call-with-input-file key-file stubs:json->scm))
           (lambda error
             (format (current-error-port)
                     (G_ "Warning: generating a new key pair."))
             (let ((k (generate-key #:n-size 2048)))
               (stubs:call-with-output-file*
                key-file
                (lambda (port)
                  (stubs:scm->json k port #:pretty #t)))
               k)))))
    (let ((alg
           (if (eq? (kty key) 'RSA)
               'RS256
               'ES256)))
      (let ((authorization-endpoint
             (make-authorization-endpoint subject 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))
            (openid-configuration
             (make-oidc-configuration jwks-uri
                                      authorization-endpoint-uri
                                      token-endpoint-uri))
            (openid-configuration-uri
             (build-uri 'https
                        #:host (uri-host issuer)
                        #:path "/.well-known/openid-configuration")))
        (lambda (request request-body)
          (let ((uri (request-uri request))
                (current-time (current-time)))
            (cond ((same-uri? uri openid-configuration-uri)
                   (let* ((current-sec (time-second current-time))
                          (exp-sec (+ current-sec 3600))
                          (exp (time-utc->date
                                (make-time time-utc 0 exp-sec))))
                     (serve-oidc-configuration exp openid-configuration)))
                  ((same-uri? uri jwks-uri)
                   (let* ((current-sec (time-second current-time))
                          (exp-sec (+ current-sec 3600))
                          (exp (time-utc->date
                                (make-time time-utc 0 exp-sec))))
                     (serve-jwks exp (make-jwks (list key)))))
                  ((same-uri? uri authorization-endpoint-uri #:skip-query #t)
                   (authorization-endpoint request request-body))
                  ((same-uri? uri token-endpoint-uri)
                   (token-endpoint request request-body))
                  ((same-uri? uri subject)
                   (values
                    (build-response #:headers '((content-type text/turtle))
                                    #:port #f)
                    (format #f
                            "@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<#~a> a foaf:Person ;
  rdfs:comment \"It works. Now you should use another service to serve that resource.\" .
"
                            (uri-fragment subject))))
                  (else
                   (values
                    (build-response #:code 404
                                    #:reason-phrase "Not Found"
                                    #:headers '((content-type application/xhtml+xml)))
                    (with-output-to-string
                      (lambda ()
                        (sxml->xml
                         `(*TOP* (*PI* xml "version=\"1.0\" encoding=\"utf-8\"")
                                 (html (@ (xmlns "http://www.w3.org/1999/xhtml")
                                          (xml:lang "en"))
                                       (body
                                        (h1 "Resource not found")
                                        (p "This OpenID Connect identity provider does not know the resource you are requesting."))))))))))))))))