Repository navigation
Expand file tree
/
Copy pathprovider.lisp
More file actions
150 lines (121 loc) · 4.75 KB
/
Copy pathprovider.lisp
File metadata and controls
150 lines (121 loc) · 4.75 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
(in-package #:saluto)
(defvar *main* "/" "Route to the parent site root")
(defclass oauth2-provider ()
((app-id
:reader app-id
:initarg :app-id)
(app-private-key
:reader app-private-key
:initarg :app-private-key)
(app-public-key
:reader app-public-key
:initarg :app-public-key)
(oauth-login-url
:reader oauth-login-url)
(access-token-query-url
:reader access-token-query-url)
(userinfo-query-url
:reader userinfo-query-url)
(name
:documentation
"The name of the provider, used in the provider routes."
:reader name
:initarg :name)))
;;;; Generics ;;;;
;;;; First request to provider
;;;; User allows app to read her data
(defgeneric build-goto-path (provider session redirect-uri)
(:documentation
"Makes a list of baseurl and parameters
to be converted to a real url."))
(defgeneric receive (provider state code error?)
(:documentation
"Receives answer from provider,
asks it for access-token, for data, and redirects to target page"))
;;;; Second request to provider
;;;; The app receives access token
(defgeneric get-access-token (provider code goto-path)
(:documentation "Retrieves access token, using received code."))
(defgeneric prepare-access-token-request (provider code goto-path)
(:documentation "DRAKMA:REQUEST is called on result of this function"))
(defgeneric extract-access-token (provider answer)
(:documentation
"Extracts access token from the provider answer"))
;;;; Third request to provider
;;;; The app receives userinfo
(defgeneric get-userinfo (provider access-token)
(:documentation
"Retrieves user info, using access token"))
(defgeneric prepare-userinfo-request (provider access-token)
(:documentation "DRAKMA:REQUEST is called on result of this function"))
(defgeneric extract-userinfo (provider request-result)
(:documentation
"Extracts user info from the provider answer"))
;;;; Common part of implementation ;;;;
;;;; First request to provider
;;;; User allows app to read her data
(defun make-goto-path (provider session redirect-uri)
"Converts list to real link"
(apply #'list-to-link (build-goto-path provider session redirect-uri)))
(defmethod build-goto-path ((provider oauth2-provider) session redirect-uri)
(list (oauth-login-url provider)
"client_id" (app-id provider)
"redirect_uri" (make-redirect-uri provider session redirect-uri)))
(defmethod receive ((provider oauth2-provider) state code error?)
(destructuring-bind (state redirect-uri) (split-state state)
(if (invalid-receiver-params? code
state
error?)
(progn (logout)
(redirect *main*))
(progn
(get-userinfo
provider
(get-access-token provider
code
(make-redirect-uri provider
state
redirect-uri)))
(redirect redirect-uri)))))
;;;; Second request to provider
;;;; The app receives access token
(defmethod get-access-token ((provider oauth2-provider) code goto-path)
(extract-access-token
provider
(request (prepare-access-token-request provider code goto-path))))
(defmethod prepare-access-token-request ((provider oauth2-provider)
code
goto-path)
(list (access-token-query-url provider)
:method :POST
:content-length t
:parameters
`(("client_id" . ,(app-id provider))
("client_secret" . ,(app-private-key provider))
("redirect_uri" . ,goto-path)
("code" . ,code))))
(defmethod extract-access-token ((provider oauth2-provider) answer)
(jsown:val (jsown:parse answer) "access_token"))
;;;; Third request to provider
;;;; The app receives userinfo
(defvar *store-userinfo-fun*
(lambda (userinfo)
(declare (ignore userinfo))
(error "SALUTO: userinfo storing not implemented"))
"To be replaced on RESTAS:MOUNT-MODULE")
(defun store-userinfo (userinfo)
(funcall *store-userinfo-fun* userinfo))
(defmethod get-userinfo ((provider oauth2-provider) access-token)
(store-userinfo
(extract-userinfo
provider
(request (prepare-userinfo-request provider access-token)))))
(defmethod prepare-userinfo-request ((provider oauth2-provider) access-token)
(list (userinfo-query-url provider)
:parameters (list (cons "access_token" access-token))
:content-length t
:method :get))
(defmethod extract-userinfo :around ((provider oauth2-provider) answer)
(append (list :session (session)
:provider (name provider))
(call-next-method provider (jsown:parse answer))))