Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
(ql:quickload '("clack" "lack" "lack-middleware-mount" "cl-dotenv" "quri" "uuid" "dexador" "jsown"))
(defpackage #:example
(:use #:common-lisp))
(in-package #:example)
(.env:load-env (merge-pathnames ".env"))
(defvar *okta-authorize-uri* (quri:uri (uiop:getenv "OKTA_AUTHORIZE_URI")))
(defvar *okta-token-uri* (quri:uri (uiop:getenv "OKTA_TOKEN_URI")))
(defvar *okta-redirect-uri* (quri:uri (uiop:getenv "OKTA_REDIRECT_URI")))
(defvar *okta-client-id* (uiop:getenv "OKTA_CLIENT_ID"))
(defvar *okta-client-secret* (uiop:getenv "OKTA_CLIENT_SECRET"))
(defvar *app*
(lack:builder
:session
(:mount "/login" 'login)
(:mount "/logout" 'logout)
(:mount (quri:uri-path *okta-redirect-uri*) 'redirect)
'main))
(defun main (env)
(list 200 '(:content-type "text/plain")
(list "main")))
(defun make-authorize-uri (state)
(let ((uri (quri:copy-uri *okta-authorize-uri*)))
(setf (quri:uri-query-params uri)
`(("client_id" . ,*okta-client-id*)
("response_type" . "code")
("redirect_uri" . ,(quri:render-uri *okta-redirect-uri*))
("scope" . "openid")
("state" . ,state)))
uri))
(defun login (env)
(let ((state (uuid:make-v4-uuid)))
(setf (gethash :app.auth-state (getf env :lack.session)) state)
(list 302 `(:location ,(make-authorize-uri state)) nil)))
(defun logout (env)
(list 200 '(:content-type "text/plain")
(list "logout")))
(defun redirect (env)
(let ((redirect-state
(ignore-errors
(uuid:make-uuid-from-string (cdr (assoc "state" (getf env :query-parameters) :test #'string=)))))
(session-state
(gethash :app.auth-state (getf env :lack.session))))
(if (and redirect-state (uuid:uuid= redirect-state session-state))
(let* ((code (cdr (assoc "code" (getf env :query-parameters) :test #'string=)))
(content `(("client_id" . ,*okta-client-id*)
("client_secret" . ,*okta-client-secret*)
("grant_type" . "authorization_code")
("code" . ,code)
("redirect_uri" . ,(quri:render-uri *okta-redirect-uri*)))))
(multiple-value-bind (body status)
(dex:post *okta-token-uri* :content content)
(if (= 200 status)
(let ((payload (jsown:parse body)))
(list 200 '(:content-type "text/plain")
(list (format nil "redirect~%~S~%~S" env payload))))
(list 400 nil nil))))
(list 400 nil nil))))
#+nil
(progn
(clack:clackup *app*))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment