anchor.diff:             (if (< rcode 400) `(302 (:location ,(format nil "~a" uri)))
                content))))))
   
  -(auth-entry-point google-login-entry *goog-endpoint-schema*)
  -(auth-entry-point facebook-login-entry *fbook-endpoint-schema*)
  -
   (defmacro def-callback-generator (name generator-args callback-args &body body)
     `(defun ,name ,generator-args
        (lambda ,callback-args
  
positive.code:          (saved-state (gethash :state session)))
      (equal saved-state received-state)))
  
  (define-condition user-not-logged-in (error) ())
  
  (defmacro my-with-context-variables ((&rest vars) &body body)
    "This improves fukamachi's version by permitting the variable to be stored somewhere
     besides the symbol corresponding to the keyword."
    `(symbol-macrolet
         ,(loop for (var key) in (ensure-mapping vars)
                for form = `(context ,(intern (string key) :keyword))
                collect `(,var ,form))
       ,@body))
  
  (defmacro ensure-logged-in (&body body)
    "Ensure that the user is logged in: otherwise throw the condition user-not-logged-in"
    (alexandria:with-gensyms (session userinfo)
      `(my-with-context-variables ((,session session))
         (with-session-values ((,userinfo userinfo)) ,session
           (if (null ,userinfo)
             (error 'user-not-logged-in)
             (progn ,@body))))))
  
  (flet
    ((handle-no-user (main-body handler-body)
       `(handler-case
          (ensure-logged-in ,@main-body)
          (user-not-logged-in (e)
                              (declare (ignorable e))
                              ,@handler-body))))
  
    (defmacro check-login (&body body)
      "Returns an HTTP 401 Error if not logged in."
      (handle-no-user body `('(401 () "Unauthorized"))))
  
    (defmacro require-login (&body body)
      "Redirects to /login if not logged in."
      (handle-no-user body
                      `((with-session-values (next-page) (context :session)
                          (setf next-page (lack.request:request-path-info *request*))
                          '(302 (:location "/login")))))))
  
  (defun load-facebook-info (loadfrom)
    (with-open-file (fbook-info (truename loadfrom))
      (let* ((data (yason:parse fbook-info))
             (client-id (gethash "client-id" data))
             (secret (gethash "secret" data)))
        (setf (client-id *FBOOK-INFO*) client-id)
        (setf (secret *FBOOK-INFO*) secret))))
  
  (defun load-google-info (loadfrom)
    (with-open-file (goog-info (truename loadfrom))
      (let* ((data (yason:parse goog-info))
             (client-id (gethash "client-id" data))
             (secret (gethash "secret" data)))
        (setf (client-id *GOOG-INFO*) client-id)
        (setf (secret *GOOG-INFO*) secret))))
  
  (defun goog-get-access-token (endpoint-schema code)
    (cl-json:decode-json-from-string
      (drakma:http-request (token-endpoint endpoint-schema)
                           :method :post
                           :redirect nil
                           :parameters `(("code" . ,code)
                                         ("client_id" . ,(client-id endpoint-schema))
                                         ("client_secret" . ,(secret endpoint-schema))
                                         ("redirect_uri" . ,(redirect-uri endpoint-schema))
                                         ("grant_type" . "authorization_code")))))
  
  (defun load-goog-endpoint-schema ()
    (discover-endpoints *goog-endpoint-schema*
                        "https://accounts.google.com/.well-known/openid-configuration"
                        :gat #'goog-get-access-token)
    (setf (redirect-uri *goog-endpoint-schema*) "http://whitespace.elangley.org/oidc_callback/google"))
  
  (sheeple:defreply get-user-info ((endpoint-schema *goog-endpoint-schema*) (access-token sheeple:=string=))
    (format t "getting user data: ~a~%" "blarg")
    (let ((endpoint (userinfo-endpoint endpoint-schema)))
      (cl-json:decode-json-from-string
        (drakma:http-request endpoint
                             :parameters `(("alt" . "json")
                                           ("access_token" . ,access-token))))))
  (defmacro auth-entry-point (name endpoint-schema)
    `(defun ,name (params)
       (declare (ignore params))
  
negative.code:              (with-session-values ,auth-session-vars ,session
                 ,@body)))))))
  
  (define-condition user-not-logged-in (error) ())
  
  (defmacro my-with-context-variables ((&rest vars) &body body)
    "This improves fukamachi's version by permitting the variable to be stored somewhere
     besides the symbol corresponding to the keyword."
    `(symbol-macrolet
         ,(loop for (var key) in (ensure-mapping vars)
                for form = `(context ,(intern (string key) :keyword))
                collect `(,var ,form))
       ,@body))
  
  (defmacro ensure-logged-in (&body body)
    "Ensure that the user is logged in: otherwise throw the condition user-not-logged-in"
    (alexandria:with-gensyms (session userinfo)
      `(my-with-context-variables ((,session session))
         (with-session-values ((,userinfo userinfo)) ,session
           (if (null ,userinfo)
             (error 'user-not-logged-in)
             (progn ,@body))))))
  
  (flet
    ((handle-no-user (main-body handler-body)
       `(handler-case
          (ensure-logged-in ,@main-body)
          (user-not-logged-in (e)
                              (declare (ignorable e))
                              ,@handler-body))))
  
    (defmacro check-login (&body body)
      "Returns an HTTP 401 Error if not logged in."
      (handle-no-user body `('(401 () "Unauthorized"))))
  
    (defmacro require-login (&body body)
      "Redirects to /login if not logged in."
      (handle-no-user body
                      `((with-session-values (next-page) (context :session)
                          (setf next-page (lack.request:request-path-info *request*))
                          '(302 (:location "/login")))))))
  
  (defun load-facebook-info (loadfrom)
    (with-open-file (fbook-info (truename loadfrom))
      (let* ((data (yason:parse fbook-info))
             (client-id (gethash "client-id" data))
             (secret (gethash "secret" data)))
        (setf (client-id *FBOOK-INFO*) client-id)
        (setf (secret *FBOOK-INFO*) secret))))
  
  (defun load-google-info (loadfrom)
    (with-open-file (goog-info (truename loadfrom))
      (let* ((data (yason:parse goog-info))
             (client-id (gethash "client-id" data))
             (secret (gethash "secret" data)))
        (setf (client-id *GOOG-INFO*) client-id)
        (setf (secret *GOOG-INFO*) secret))))
  
  (defun goog-get-access-token (endpoint-schema code)
    (cl-json:decode-json-from-string
      (drakma:http-request (token-endpoint endpoint-schema)
                           :method :post
                           :redirect nil
                           :parameters `(("code" . ,code)
                                         ("client_id" . ,(client-id endpoint-schema))
                                         ("client_secret" . ,(secret endpoint-schema))
                                         ("redirect_uri" . ,(redirect-uri endpoint-schema))
                                         ("grant_type" . "authorization_code")))))
  
  (defun load-goog-endpoint-schema ()
    (discover-endpoints *goog-endpoint-schema*
                        "https://accounts.google.com/.well-known/openid-configuration"
                        :gat #'goog-get-access-token)
    (setf (redirect-uri *goog-endpoint-schema*) "http://whitespace.elangley.org/oidc_callback/google"))
  
  (sheeple:defreply get-user-info ((endpoint-schema *goog-endpoint-schema*) (access-token sheeple:=string=))
    (format t "getting user data: ~a~%" "blarg")
    (let ((endpoint (userinfo-endpoint endpoint-schema)))
      (cl-json:decode-json-from-string
        (drakma:http-request endpoint
                             :parameters `(("alt" . "json")
                                           ("access_token" . ,access-token))))))
  
  (auth-entry-point google-login-entry *goog-endpoint-schema*)
  (auth-entry-point facebook-login-entry *fbook-endpoint-schema*)
  
  (flet ((get-code (params) (assoc-cdr "code" params #'equal)))
  
    (def-callback-generator google-callback (get-app-user-cb) (params)
  
      (labels ((get-real-access-token (a-t) (assoc-cdr :access--token a-t))
               (get-id-token (a-t) (cljwt:decode (assoc-cdr :id--token a-t) :fail-if-unsupported nil))
               (get-login-data (a-t)
                 (let ((access-token (get-real-access-token a-t)))
                   (values access-token
                           (get-user-info *goog-endpoint-schema* access-token)
                           (get-id-token a-t)))))
  
        (let ((a-t (get-access-token *goog-endpoint-schema* (get-code params))))
          (auth-callback-skeleton params (:endpoint-schema *goog-endpoint-schema*
                                          :auth-session-vars (accesstoken userinfo idtoken app-user))
            (multiple-value-bind (access-token user-info id-token) (get-login-data a-t)
              (setf
                accesstoken access-token
                userinfo user-info
                idtoken id-token
                app-user (funcall get-app-user-cb user-info id-token access-token)))
            '(302 (:location "/"))))))
  
    (def-callback-generator facebook-callback (get-app-user-cb) (params)
  