├── .gitattributes ├── north-logo.png ├── doc ├── north-logo.png ├── north-drakma │ ├── north-logo.png │ └── index.html ├── north-dexador │ ├── north-logo.png │ └── index.html └── index.html ├── example ├── package.lisp ├── north-example.asd ├── client.lisp ├── authorize.ctml └── server.lisp ├── staple.ext.lisp ├── README.md ├── north.asd ├── north-drakma.asd ├── north-dexador.asd ├── drakma.lisp ├── LICENSE ├── north-core.asd ├── dexador.lisp ├── package.lisp ├── request.lisp ├── conditions.lisp ├── client.lisp ├── server.lisp ├── toolkit.lisp └── documentation.lisp /.gitattributes: -------------------------------------------------------------------------------- 1 | doc/ linguist-vendored 2 | 3 | doc/ linguist-vendored 4 | -------------------------------------------------------------------------------- /north-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shinmera/north/HEAD/north-logo.png -------------------------------------------------------------------------------- /doc/north-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shinmera/north/HEAD/doc/north-logo.png -------------------------------------------------------------------------------- /doc/north-drakma/north-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shinmera/north/HEAD/doc/north-drakma/north-logo.png -------------------------------------------------------------------------------- /doc/north-dexador/north-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shinmera/north/HEAD/doc/north-dexador/north-logo.png -------------------------------------------------------------------------------- /example/package.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:cl-user) 2 | (defpackage #:north-example 3 | (:nicknames #:org.shirakumo.north.example) 4 | (:use #:cl) 5 | (:export 6 | #:start 7 | #:stop)) 8 | -------------------------------------------------------------------------------- /staple.ext.lisp: -------------------------------------------------------------------------------- 1 | 2 | (defmethod staple:packages ((system (eql (asdf:find-system :north)))) 3 | (mapcar #'find-package '(:north))) 4 | 5 | (defmethod staple:subsystems ((system (eql (asdf:find-system :north)))) 6 | (mapcar #'asdf:find-system '(:north-drakma :north-dexador))) 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository has [moved](https://shinmera.com/projects/north)! 2 | Due to Microsoft's continued enshittification of the platform this repository has been moved to [Codeberg](https://shinmera.com/projects/north) in August of 2025. It will not receive further updates or patches. **Issues and pull requests will not be looked at here either**, please submit your patches and issue tickets on Codeberg, or send them directly via good old email patches to [shirakumo@tymoon.eu](mailto:shirakumo@tymoon.eu). 3 | 4 | Thanks. -------------------------------------------------------------------------------- /north.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem north 2 | :version "1.0.0" 3 | :license "zlib" 4 | :author "Yukari Hafner " 5 | :maintainer "Yukari Hafner " 6 | :description "oAuth 1.0a server and client implementation, the successor to South." 7 | :homepage "https://shinmera.com/docs/north/" 8 | :bug-tracker "https://shinmera.com/project/north/issues" 9 | :source-control (:git "https://shinmera.com/project/north.git") 10 | :serial T 11 | :components () 12 | :depends-on (:north-drakma)) 13 | -------------------------------------------------------------------------------- /north-drakma.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem north-drakma 2 | :version "1.0.0" 3 | :license "zlib" 4 | :author "Yukari Hafner " 5 | :maintainer "Yukari Hafner " 6 | :description "Drakma backend for North" 7 | :homepage "https://shinmera.com/docs/north/" 8 | :bug-tracker "https://shinmera.com/project/north/issues" 9 | :source-control (:git "https://shinmera.com/project/north.git") 10 | :serial T 11 | :components ((:file "drakma")) 12 | :depends-on (:drakma 13 | :north-core)) 14 | -------------------------------------------------------------------------------- /north-dexador.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem north-dexador 2 | :version "1.0.0" 3 | :license "zlib" 4 | :author "Yukari Hafner " 5 | :maintainer "Yukari Hafner " 6 | :description "Dexador backend for North" 7 | :homepage "https://shinmera.com/docs/north/" 8 | :bug-tracker "https://shinmera.com/project/north/issues" 9 | :source-control (:git "https://shinmera.com/project/north.git") 10 | :serial T 11 | :components ((:file "dexador")) 12 | :depends-on (:dexador 13 | :north-core)) 14 | -------------------------------------------------------------------------------- /example/north-example.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem north-example 2 | :version "1.0.0" 3 | :license "zlib" 4 | :author "Yukari Hafner " 5 | :maintainer "Yukari Hafner " 6 | :description "An example illustrating the use of North." 7 | :homepage "https://shinmera.com/docs/north/" 8 | :bug-tracker "https://shinmera.com/project/north/issues" 9 | :source-control (:git "https://shinmera.com/project/north.git") 10 | :serial T 11 | :components ((:file "package") 12 | (:file "server") 13 | (:file "client")) 14 | :depends-on (:north 15 | :drakma 16 | :hunchentoot 17 | :clip)) 18 | -------------------------------------------------------------------------------- /example/client.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north.example) 2 | 3 | (defparameter *client* (make-instance 'north:client 4 | :key (north:key *application*) 5 | :secret (north:secret *application*) 6 | :callback "oob" 7 | :request-token-uri "http://localhost:4242/oauth/request-token" 8 | :authorize-uri "http://localhost:4242/oauth/authorize" 9 | :access-token-uri "http://localhost:4242/oauth/access-token" 10 | :verify-uri "http://localhost:4242/oauth/verify")) 11 | -------------------------------------------------------------------------------- /drakma.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (defmethod call ((request request) &rest drakma-args) 4 | (let ((drakma:*text-content-types* 5 | (list* '("application" . "x-www-form-urlencoded") 6 | drakma:*text-content-types*))) 7 | (multiple-value-bind (body status-code headers) 8 | (apply #'drakma:http-request 9 | (url request) 10 | :method (http-method request) 11 | :parameters (parameters request) 12 | :additional-headers (headers request) 13 | :url-encoder #'url-encode 14 | :external-format-in *external-format* 15 | :external-format-out *external-format* 16 | drakma-args) 17 | (unless (<= 200 status-code 299) 18 | (error 'request-failed :request request :body body :status-code status-code :headers headers)) 19 | body))) 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Yukari Hafner 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /north-core.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem north-core 2 | :version "1.0.0" 3 | :license "zlib" 4 | :author "Yukari Hafner " 5 | :maintainer "Yukari Hafner " 6 | :description "Core functionality of North, excluding the HTTP backend." 7 | :homepage "https://shinmera.com/docs/north/" 8 | :bug-tracker "https://shinmera.com/project/north/issues" 9 | :source-control (:git "https://shinmera.com/project/north.git") 10 | :serial T 11 | :components ((:file "package") 12 | (:file "toolkit") 13 | (:file "conditions") 14 | (:file "request") 15 | (:file "server") 16 | (:file "client") 17 | (:file "documentation")) 18 | :depends-on (:frugal-uuid 19 | :babel 20 | :ironclad/mac/hmac 21 | :ironclad/digest/sha1 22 | :cl-base64 23 | :cl-ppcre 24 | :documentation-utils)) 25 | -------------------------------------------------------------------------------- /dexador.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (defun parameters->string (params) 4 | (with-output-to-string (out) 5 | (loop for cons on params 6 | for param = (car cons) 7 | do (format out "~a=~a" (url-encode (car param)) (url-encode (cdr param))) 8 | (when (cdr cons) (format out "&"))))) 9 | 10 | (defun convert-received-headers (headers) 11 | (loop for k being the hash-keys of headers 12 | for v being the hash-values of headers 13 | collect (cons (intern (string-upcase k) :keyword) v))) 14 | 15 | (defun purify-form-data (params) 16 | (loop for param in params 17 | collect (if (listp (cdr param)) 18 | (destructuring-bind (key content &key content-type &allow-other-keys) param 19 | (list key content :content-type content-type)) 20 | param))) 21 | 22 | (defmethod call ((request request) &rest dexador-args &key form-data) 23 | (let ((params (unless form-data (quri:make-uri :query (parameters->string (parameters request))))) 24 | (uri (quri:uri (url request)))) 25 | (multiple-value-bind (body status-code headers) 26 | (handler-bind ((dexador:http-request-failed #'dexador:ignore-and-continue)) 27 | (apply #'dexador:request 28 | (if form-data uri (quri:merge-uris params uri)) 29 | :method (http-method request) 30 | :headers (append (headers request) 31 | (when form-data `(("content-type" . "multipart/mixed")))) 32 | :content (when form-data (purify-form-data (parameters request))) 33 | (loop for (key val) on dexador-args by #'cddr 34 | unless (eql key :form-data) collect key 35 | unless (eql key :form-data) collect val))) 36 | (unless (<= 200 status-code 299) 37 | (error 'request-failed :request request :body body :status-code status-code :headers headers)) 38 | body))) 39 | 40 | -------------------------------------------------------------------------------- /package.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:cl-user) 2 | (defpackage #:north 3 | (:nicknames #:org.shirakumo.north) 4 | (:use #:cl) 5 | ;; client.lisp 6 | (:export 7 | #:call 8 | #:call-signed 9 | #:make-signed-request 10 | #:make-signed-data-request 11 | #:initiate-authentication 12 | #:complete-authentication 13 | #:client 14 | #:key 15 | #:secret 16 | #:token 17 | #:token-secret 18 | #:callback 19 | #:request-token-uri 20 | #:authorize-uri 21 | #:access-token-uri 22 | #:verify-uri 23 | #:user-agent) 24 | ;; conditions.lisp 25 | (:export 26 | #:north-condition 27 | #:request 28 | #:parameter-error 29 | #:verification-error 30 | #:client-error 31 | #:parameters-missing 32 | #:parameters 33 | #:bad-version 34 | #:verifier-taken 35 | #:nonce-reused 36 | #:invalid-signature 37 | #:invalid-verifier 38 | #:invalid-token 39 | #:invalid-application 40 | #:request-failed 41 | #:body 42 | #:status-code 43 | #:headers 44 | #:callback-unconfirmed) 45 | ;; request.lisp 46 | (:export 47 | #:make-signed 48 | #:make-authorized 49 | #:verify 50 | #:request 51 | #:http-method 52 | #:url 53 | #:parameters 54 | #:headers 55 | #:oauth 56 | #:make-request) 57 | ;; server.lisp 58 | (:export 59 | #:make-application 60 | #:make-session 61 | #:application 62 | #:session 63 | #:rehash-session 64 | #:revoke-application 65 | #:revoke-session 66 | #:record-nonce 67 | #:find-nonce 68 | #:oauth/request-token 69 | #:oauth/authorize 70 | #:oauth/access-token 71 | #:oauth/verify 72 | #:session 73 | #:token 74 | #:token-secret 75 | #:verifier 76 | #:callback 77 | #:key 78 | #:access 79 | #:application 80 | #:key 81 | #:secret 82 | #:name 83 | #:server 84 | #:simple-server 85 | #:applications 86 | #:sessions 87 | #:nonces) 88 | ;; toolkit.lisp 89 | (:export 90 | #:*external-format* 91 | #:pget 92 | #:url-encode 93 | #:url-decode 94 | #:sign 95 | #:make-nonce 96 | #:make-timestamp 97 | #:sort-params 98 | #:concat-params 99 | #:normalize-url 100 | #:create-signature 101 | #:destructure-oauth-header 102 | #:oauth-response->alist 103 | #:alist->oauth-response)) 104 | -------------------------------------------------------------------------------- /request.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (defgeneric make-signed (request consumer-secret &optional token-secret)) 4 | (defgeneric make-authorized (request)) 5 | (defgeneric verify (request consumer-secret &optional token-secret)) 6 | 7 | (defclass request () 8 | ((http-method :initarg :http-method :accessor http-method) 9 | (url :initarg :url :accessor url) 10 | (parameters :initarg :parameters :accessor parameters) 11 | (headers :initarg :headers :accessor headers) 12 | (oauth :initarg :oauth :accessor oauth)) 13 | (:default-initargs 14 | :http-method :GET 15 | :url "http://example.com" 16 | :parameters () 17 | :headers () 18 | :oauth ())) 19 | 20 | (defmethod initialize-instance :after ((request request) &key) 21 | (macrolet ((default (key val) 22 | `(or (pget ,key (oauth request)) 23 | (setf (pget ,key (oauth request)) ,val)))) 24 | (default :oauth_nonce (make-nonce)) 25 | (default :oauth_signature_method "HMAC-SHA1") 26 | (default :oauth_timestamp (make-timestamp)) 27 | (default :oauth_version "1.0")) 28 | (when (pget "Authorization" (headers request)) 29 | (setf (oauth request) (destructure-oauth-header (pget "Authorization" (headers request)))))) 30 | 31 | (defun make-request (url method &key params headers oauth) 32 | (make-instance 'request :url url :http-method method :parameters params :headers headers :oauth oauth)) 33 | 34 | (defmethod make-signed ((request request) consumer-secret &optional token-secret) 35 | (setf (oauth request) (remove-duplicates (remove NIL (oauth request) :key #'cdr) 36 | :from-end T :key #'car :test #'string-equal)) 37 | (setf (pget :oauth_signature (oauth request)) 38 | (create-signature consumer-secret token-secret (http-method request) 39 | (url request) (oauth request) (parameters request))) 40 | request) 41 | 42 | (defmethod make-authorized ((request request)) 43 | (unless (pget :oauth_signature (oauth request)) 44 | (error "Request ~a must be signed first!" request)) 45 | (setf (pget "Authorization" (headers request)) 46 | (format NIL "OAuth ~a" (concat-params (oauth request) :quote T :delim ", "))) 47 | request) 48 | 49 | (defmethod verify ((request request) consumer-secret &optional token-secret) 50 | (let* ((oauths (destructure-oauth-header (pget "Authorization" (headers request)))) 51 | (old-sig (pget :oauth_signature oauths)) 52 | (new-sig (create-signature consumer-secret token-secret (http-method request) 53 | (url request) (oauth request) (parameters request)))) 54 | 55 | (string= old-sig new-sig))) 56 | -------------------------------------------------------------------------------- /example/authorize.ctml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Authorize User Account Access 6 | 65 | 66 | 67 |
68 |

Autorize User Account Access

69 |
70 |
71 | 72 |
Some error occurred maybe idunno.
73 |
74 | 75 |
76 | Please enter the following verifier into your application: 77 | VERIFIER 78 |
79 |
80 | 81 | Are you sure you want to allow STUFF access to your account? 82 |
83 | 84 | 85 | 86 |
87 |
88 |
89 | 90 | 91 | -------------------------------------------------------------------------------- /conditions.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (define-condition north-condition (condition) 4 | ((request :initarg :request :reader request)) 5 | (:default-initargs :request (error "REQUEST required."))) 6 | 7 | (define-condition parameter-error (north-condition error) 8 | ()) 9 | 10 | (define-condition verification-error (north-condition error) 11 | ()) 12 | 13 | (define-condition client-error (north-condition error) 14 | ()) 15 | 16 | (define-condition parameters-missing (parameter-error) 17 | ((parameters :initarg :parameters :reader parameters)) 18 | (:default-initargs :parameters (error "PARAMETERS required.")) 19 | (:report (lambda (c s) (format s "The oauth parameters ~s are required but from the request." 20 | (parameters c))))) 21 | 22 | (define-condition bad-version (parameter-error) 23 | () 24 | (:report (lambda (c s) (format s "The version ~s is not supported, must be \"1.0\"." 25 | (pget :oauth_version (oauth (request c))))))) 26 | 27 | (define-condition verifier-taken (verification-error) 28 | () 29 | (:report (lambda (c s) (format s "The verifier for the request token ~s has already been generated." 30 | (pget :oauth_token (oauth (request c))))))) 31 | 32 | (define-condition nonce-reused (verification-error) 33 | () 34 | (:report (lambda (c s) (format s "The nonce ~s has already been used." 35 | (pget :oauth_nonce (oauth (request c))))))) 36 | 37 | (define-condition invalid-signature (verification-error) 38 | () 39 | (:report (lambda (c s) (format s "Unable to verify the signature of the request.")))) 40 | 41 | (define-condition invalid-verifier (verification-error) 42 | () 43 | (:report (lambda (c s) (format s "The verifier ~s is invalid for this request token." 44 | (pget :oauth_verifier (oauth (request c))))))) 45 | 46 | (define-condition invalid-token (verification-error) 47 | () 48 | (:report (lambda (c s) (format s "The token ~s is invalid." 49 | (pget :oauth_token (oauth (request c))))))) 50 | 51 | (define-condition invalid-application (verification-error) 52 | () 53 | (:report (lambda (c s) (format s "No application with key ~s." 54 | (pget :oauth_consumer_key (oauth (request c))))))) 55 | 56 | (define-condition request-failed (client-error) 57 | ((body :initarg :body :reader body) 58 | (status-code :initarg :status-code :reader status-code) 59 | (headers :initarg :headers :reader headers)) 60 | (:default-initargs 61 | :body (error "BODY required.") 62 | :status-code (error "STATUS-CODE required.") 63 | :headers (error "HEADERS required.")) 64 | (:report (lambda (c s) (format s "~a Request to ~a failed with status code ~a." 65 | (http-method (request c)) (url (request c)) (status-code c))))) 66 | 67 | (define-condition callback-unconfirmed (client-error) 68 | () 69 | (:report (lambda (c s) (format s "Callback was not confirmed on token request.")))) 70 | -------------------------------------------------------------------------------- /example/server.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north.example) 2 | 3 | (defvar *server* (make-instance 'north:simple-server)) 4 | (defvar *hunchentoot* (make-instance 'hunchentoot:easy-acceptor :port 4242)) 5 | (defvar *authorize-page* (asdf:system-relative-pathname :north-example "authorize.ctml")) 6 | 7 | (defvar *application* (north:make-application *server* :name "North")) 8 | 9 | (defun start () 10 | (hunchentoot:start *hunchentoot*)) 11 | 12 | (defun stop () 13 | (hunchentoot:stop *hunchentoot*)) 14 | 15 | (defmacro respond (&rest stuff) 16 | `(progn 17 | (setf (hunchentoot:content-type*) "text/plain") 18 | (north:alist->oauth-response 19 | (list 20 | ,@(loop for (key val) in stuff 21 | collect `(cons ,key ,val)))))) 22 | 23 | (defun error-out (err) 24 | (respond 25 | ("status" "error") 26 | ("error_type" (string (type-of err))) 27 | ("error_text" (let ((*print-escape* NIL)) (write-to-string err))))) 28 | 29 | (defmacro with-error-handling (() &body body) 30 | `(handler-case 31 | (progn ,@body) 32 | (north:parameter-error (err) 33 | (setf (hunchentoot:return-code*) 400) 34 | (error-out err)) 35 | (north:verification-error (err) 36 | (setf (hunchentoot:return-code*) 401) 37 | (error-out err)) 38 | (error (err) 39 | (setf (hunchentoot:return-code*) 500) 40 | (error-out err)))) 41 | 42 | (defun make-request (request) 43 | (make-instance 44 | 'north:request 45 | :http-method (hunchentoot:request-method* request) 46 | :url (north::normalize-url 47 | (format NIL "http://~a/~a" 48 | (hunchentoot:host request) (hunchentoot:request-uri* request))) 49 | :parameters (append (hunchentoot:post-parameters* request) 50 | (hunchentoot:get-parameters* request)) 51 | :headers (hunchentoot:headers-in* request))) 52 | 53 | (hunchentoot:define-easy-handler (oauth/request-token :uri "/oauth/request-token") () 54 | (with-error-handling () 55 | (multiple-value-bind (token secret callback-confirmed) (north:oauth/request-token *server* (make-request hunchentoot:*request*)) 56 | (respond 57 | ("oauth_token" token) 58 | ("oauth_token_secret" secret) 59 | ("oauth_callback_confirmed" (if callback-confirmed :true :false)))))) 60 | 61 | (hunchentoot:define-easy-handler (oauth/authorize :uri "/oauth/authorize") (oauth_token verifier error) 62 | (with-error-handling () 63 | (let* ((session (or (north:session *server* oauth_token) 64 | (error 'north:invalid-token :request (make-request hunchentoot:*request*)))) 65 | (application (north:application *server* (north:key session)))) 66 | (setf (hunchentoot:content-type*) "application/xhtml+xml") 67 | (plump:serialize 68 | (clip:process *authorize-page* 69 | :oauth_token oauth_token 70 | :application application 71 | :verifier verifier 72 | :error error) 73 | NIL)))) 74 | 75 | (hunchentoot:define-easy-handler (oauth/authenticate :uri "/oauth/authenticate") (oauth-token action) 76 | (with-error-handling () 77 | (cond ((string= action "Deny") 78 | "Shiet") 79 | ((string= action "Allow") 80 | (multiple-value-bind (token verifier url) (north:oauth/authorize *server* (make-request hunchentoot:*request*)) 81 | (if url 82 | (hunchentoot:redirect url) 83 | (hunchentoot:redirect (format NIL"/oauth/authorize?oauth_token=~a&verifier=~a" 84 | (north:url-encode token) (north:url-encode verifier)))))) 85 | (T 86 | (hunchentoot:redirect "/oauth/authorize?error=Invalid%20action."))))) 87 | 88 | (hunchentoot:define-easy-handler (oauth/access-token :uri "/oauth/access-token") () 89 | (with-error-handling () 90 | (multiple-value-bind (token secret) (north:oauth/access-token *server* (make-request hunchentoot:*request*)) 91 | (respond 92 | ("oauth_token" token) 93 | ("oauth_token_secret" secret))))) 94 | 95 | (hunchentoot:define-easy-handler (oauth/verify :uri "/oauth/verify") () 96 | (with-error-handling () 97 | (north:oauth/verify *server* (make-request hunchentoot:*request*)) 98 | (respond 99 | ("status" "success")))) 100 | 101 | -------------------------------------------------------------------------------- /client.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (defgeneric call (request &rest args)) 4 | (defgeneric call-signed (request consumer-secret &optional token-secret &rest args)) 5 | (defgeneric make-signed-request (client url method &key params headers oauth)) 6 | (defgeneric make-signed-data-request (client url data &key params headers oauth)) 7 | (defgeneric initiate-authentication (client)) 8 | (defgeneric complete-authentication (client verifier &optional token)) 9 | 10 | (defmethod call-signed ((request request) consumer-secret &optional token-secret &rest args) 11 | (apply #'call (make-authorized (make-signed request consumer-secret token-secret)) args)) 12 | 13 | (defclass client () 14 | ((key :initarg :key :accessor key) 15 | (secret :initarg :secret :accessor secret) 16 | (token :initarg :token :accessor token) 17 | (token-secret :initarg :token-secret :accessor token-secret) 18 | (callback :initarg :callback :accessor callback) 19 | (request-token-uri :initarg :request-token-uri :accessor request-token-uri) 20 | (authorize-uri :initarg :authorize-uri :accessor authorize-uri) 21 | (access-token-uri :initarg :access-token-uri :accessor access-token-uri) 22 | (verify-uri :initarg :verify-uri :accessor verify-uri) 23 | (user-agent :initarg :user-agent :accessor user-agent)) 24 | (:default-initargs 25 | :key (error "KEY required. This is your oAuth application's key.") 26 | :secret (error "SECRET required. This is your oAuth application's secret.") 27 | :token NIL 28 | :token-secret NIL 29 | :callback "oob" 30 | :request-token-uri (error "REQUEST-TOKEN-URI required.") 31 | :authorize-uri (error "AUTHORIZE-URI required.") 32 | :access-token-uri (error "ACCESS-TOKEN-URI required.") 33 | :verify-uri NIL 34 | :user-agent (user-agent T))) 35 | 36 | (defmethod make-load-form ((client client) &optional env) 37 | (declare (ignore env)) 38 | `(make-instance 39 | 'client 40 | :key ,(key client) 41 | :secret ,(secret client) 42 | :token ,(token client) 43 | :token-secret ,(token-secret client) 44 | :callback ,(callback client) 45 | :request-token-uri ,(request-token-uri client) 46 | :authorize-uri ,(authorize-uri client) 47 | :access-token-uri ,(access-token-uri client) 48 | :verify-uri ,(verify-uri client))) 49 | 50 | (defmethod make-signed-request ((client client) url method &key params headers oauth) 51 | (let ((request (make-request url method :params params :headers headers 52 | :oauth `(,@oauth 53 | (:oauth_consumer_key . ,(key client)) 54 | (:oauth_token . ,(token client)))))) 55 | (values (call-signed request (secret client) (token-secret client)) 56 | request))) 57 | 58 | (defmethod make-signed-data-request ((client client) url data &key params headers oauth) 59 | (let ((request (make-request url :post :headers headers 60 | :oauth `(,@oauth 61 | (:oauth_consumer_key . ,(key client)) 62 | (:oauth_token . ,(token client)))))) 63 | (make-authorized (make-signed request (secret client) (token-secret client))) 64 | (setf (parameters request) 65 | (append params 66 | (loop for (k . v) in data 67 | collect (destructuring-bind (file &key (content-type "application/octet-stream") filename) 68 | (if (consp v) v (list v)) 69 | (list k file :content-type content-type :filename filename))))) 70 | (values (call request :form-data T :user-agent (user-agent client)) 71 | request))) 72 | 73 | (defmethod initiate-authentication ((client client)) 74 | (setf (token client) NIL) 75 | (setf (token-secret client) NIL) 76 | (multiple-value-bind (result request) (make-signed-request client (request-token-uri client) :post 77 | :oauth `((:oauth_callback . ,(callback client)))) 78 | (let ((result (oauth-response->alist result))) 79 | (unless (string-equal "true" (pget :oauth_callback_confirmed result)) 80 | (error 'callback-unconfirmed :request request)) 81 | (setf (token client) (pget :oauth_token result)) 82 | (setf (token-secret client) (pget :oauth_token_secret result)) 83 | (format NIL "~a?oauth_token=~a" 84 | (authorize-uri client) (url-encode (token client)))))) 85 | 86 | (defmethod complete-authentication ((client client) verifier &optional (token (token client))) 87 | (let ((result (make-signed-request client (access-token-uri client) :post 88 | :oauth `((:oauth_verifier . ,verifier) 89 | (:oauth_token . ,token))))) 90 | (let ((result (oauth-response->alist result))) 91 | (setf (token client) (pget :oauth_token result)) 92 | (setf (token-secret client) (pget :oauth_token_secret result)) 93 | (when (verify-uri client) 94 | (make-signed-request client (verify-uri client) :post)) 95 | client))) 96 | -------------------------------------------------------------------------------- /doc/north-dexador/index.html: -------------------------------------------------------------------------------- 1 | North Dexador -------------------------------------------------------------------------------- /doc/north-drakma/index.html: -------------------------------------------------------------------------------- 1 | North Drakma -------------------------------------------------------------------------------- /server.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (defgeneric make-application (server &key &allow-other-keys)) 4 | (defgeneric make-session (server application callback &key access &allow-other-keys)) 5 | (defgeneric application (server application-key)) 6 | (defgeneric session (server token)) 7 | (defgeneric rehash-session (server session)) 8 | (defgeneric revoke-application (server application-key)) 9 | (defgeneric revoke-session (server token)) 10 | (defgeneric record-nonce (server timestamp nonce)) 11 | (defgeneric find-nonce (server timestamp nonce)) 12 | (defgeneric oauth/request-token (server request)) 13 | (defgeneric oauth/authorize (server request)) 14 | (defgeneric oauth/access-token (server request)) 15 | (defgeneric oauth/verify (server request)) 16 | 17 | (defclass session () 18 | ((token :initarg :token :accessor token) 19 | (token-secret :initarg :token-secret :accessor token-secret) 20 | (verifier :initarg :verifier :accessor verifier) 21 | (callback :initarg :callback :accessor callback) 22 | (key :initarg :key :accessor key) 23 | (access :initarg :access :accessor access)) 24 | (:default-initargs 25 | :token (make-nonce) 26 | :token-secret (make-nonce) 27 | :verifier (make-nonce) 28 | :callback "oob" 29 | :key (error "KEY required.") 30 | :access :request)) 31 | 32 | (defmethod print-object ((session session) stream) 33 | (print-unreadable-object (session stream :type T) 34 | (format stream "~a/~a" (key session) (token session)))) 35 | 36 | (defclass application () 37 | ((key :initarg :key :accessor key) 38 | (secret :initarg :secret :accessor secret) 39 | (name :initarg :name :accessor name)) 40 | (:default-initargs 41 | :key (make-nonce) 42 | :secret (make-nonce) 43 | :name (error "NAME required."))) 44 | 45 | (defmethod print-object ((application application) stream) 46 | (print-unreadable-object (application stream :type T) 47 | (format stream "~s ~a" (name application) (key application)))) 48 | 49 | (defclass server () 50 | ()) 51 | 52 | (defmethod make-session ((server server) (application-key string) callback &rest args) 53 | (apply #'make-session server (application server application-key) callback args)) 54 | 55 | (defmethod rehash-session ((server server) (string string)) 56 | (rehash-session server (session server string))) 57 | 58 | (defmethod revoke-application ((server server) (string string)) 59 | (revoke-application server (application server string))) 60 | 61 | (defmethod revoke-session ((server server) (string string)) 62 | (revoke-session server (session server string))) 63 | 64 | (defmethod find-nonce ((server server) (timestamp string) nonce) 65 | (find-nonce server (parse-integer timestamp) nonce)) 66 | 67 | (defun check-parameters-present (request &rest parameters) 68 | (loop for param in parameters 69 | unless (pget param (oauth request)) 70 | collect param into missing 71 | finally (when missing (error 'parameters-missing :request request :parameters missing)))) 72 | 73 | (defun check-nonce (request server) 74 | (let ((nonce (pget :oauth_nonce (oauth request))) 75 | (timestamp (pget :oauth_timestamp (oauth request)))) 76 | (if (find-nonce server timestamp nonce) 77 | (error 'nonce-reused :request request) 78 | (record-nonce server timestamp nonce)))) 79 | 80 | (defun check-version (request) 81 | (let ((version (pget :oauth_version (oauth request)))) 82 | (unless (or (not version) (string= "1.0" version)) 83 | (error 'bad-version :request request)))) 84 | 85 | (defun check-request (request server &key session application) 86 | (let ((session (or session (session server (pget :oauth_token (oauth request))))) 87 | (application (or application (application server (pget :oauth_consumer_key (oauth request)))))) 88 | (check-version request) 89 | (check-nonce request server) 90 | (unless application 91 | (error 'invalid-application :request request)) 92 | (unless (verify request (secret application) (when session (token-secret session))) 93 | (error 'invalid-signature :request request)) 94 | (values session application))) 95 | 96 | (defun check-verifier (request server &key session) 97 | (let ((verifier (pget :oauth_verifier (oauth request))) 98 | (session (or session (session server (pget :oauth_token (oauth request)))))) 99 | (unless (and session (string= verifier (verifier session))) 100 | (error 'invalid-verifier :request request)) 101 | session)) 102 | 103 | (defun check-token (request server &key session) 104 | (let ((session (or session (session server (or (pget :oauth_token (oauth request)) 105 | (pget :oauth_token (parameters request))))))) 106 | (unless (and session (token session)) 107 | (error 'invalid-token :request request)) 108 | session)) 109 | 110 | (defmethod oauth/request-token ((server server) (request request)) 111 | (check-parameters-present request 112 | :oauth_consumer_key :oauth_signature_method :oauth_signature 113 | :oauth_timestamp :oauth_nonce :oauth_callback) 114 | (check-request request server) 115 | (let ((session (make-session server 116 | (pget :oauth_consumer_key (oauth request)) 117 | (pget :oauth_callback (oauth request)) 118 | :access :request))) 119 | (values (token session) (token-secret session) T session))) 120 | 121 | (defmethod oauth/authorize ((server server) (request request)) 122 | (check-token request server) 123 | (let* ((session (session server (pget :oauth_token (parameters request)))) 124 | (verifier (verifier session)) 125 | (token (token session)) 126 | (callback (callback session))) 127 | (unless verifier 128 | (error 'verifier-taken :request request)) 129 | (values token 130 | verifier 131 | (when (string/= callback "oob") 132 | (format NIL "~a?oauth_token=~a&oauth_verifier=~a" 133 | callback (url-encode token) (url-encode verifier))) 134 | session))) 135 | 136 | (defmethod oauth/access-token ((server server) (request request)) 137 | (check-parameters-present request 138 | :oauth_consumer_key :oauth_signature_method :oauth_signature 139 | :oauth_timestamp :oauth_nonce :oauth_token :oauth_verifier) 140 | (let ((session (check-token request server))) 141 | (check-verifier request server :session session) 142 | (check-request request server :session session) 143 | ;; Invalidate verifier 144 | (setf (verifier session) NIL) 145 | (setf (access session) :access) 146 | (rehash-session server session) 147 | (values (token session) 148 | (token-secret session) 149 | session))) 150 | 151 | (defmethod oauth/verify ((server server) (request request)) 152 | (check-parameters-present request 153 | :oauth_consumer_key :oauth_signature_method :oauth_signature 154 | :oauth_timestamp :oauth_nonce :oauth_token) 155 | (let ((session (check-token request server))) 156 | (check-request request server :session session))) 157 | 158 | (defclass simple-server (server) 159 | ((applications :initform (make-hash-table :test 'equal) :accessor applications) 160 | (sessions :initform (make-hash-table :test 'equal) :accessor sessions) 161 | (nonces :initform (make-hash-table :test 'eql) :accessor nonces))) 162 | 163 | (defmethod make-application ((server simple-server) &key name) 164 | (let ((application (make-instance 'application :name name))) 165 | (setf (gethash (key application) (applications server)) application) 166 | application)) 167 | 168 | (defmethod make-session ((server simple-server) (application application) callback &key (access :request)) 169 | (let ((session (make-instance 'session :key (key application) :access access))) 170 | (setf (gethash (token session) (sessions server)) session) 171 | session)) 172 | 173 | (defmethod application ((server simple-server) application-key) 174 | (gethash application-key (applications server))) 175 | 176 | (defmethod session ((server simple-server) token) 177 | (gethash token (sessions server))) 178 | 179 | (defmethod rehash-session ((server simple-server) (session session)) 180 | (revoke-session server session) 181 | (setf (token session) (make-nonce)) 182 | (setf (token-secret session) (make-nonce)) 183 | (setf (gethash (token session) (sessions server)) session)) 184 | 185 | (defmethod revoke-application ((server simple-server) (application application)) 186 | (remhash (key application) (applications server)) 187 | application) 188 | 189 | (defmethod revoke-session ((server simple-server) (session session)) 190 | (remhash (token session) (sessions server)) 191 | session) 192 | 193 | (defmethod record-nonce ((server simple-server) timestamp nonce) 194 | (push nonce (gethash timestamp (nonces server))) 195 | nonce) 196 | 197 | (defmethod find-nonce ((server simple-server) (timestamp integer) (nonce string)) 198 | (find nonce (gethash timestamp (nonces server)) :test #'equal)) 199 | 200 | -------------------------------------------------------------------------------- /toolkit.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | (defvar *external-format* :utf-8) 4 | 5 | (defun alist-key (thing) 6 | (etypecase thing 7 | (string thing) 8 | (symbol (string-downcase thing)))) 9 | 10 | (defun pget (key alist) 11 | (cdr (assoc key alist :test #'string-equal))) 12 | 13 | (define-setf-expander pget (key alist) 14 | (let ((cell (gensym "CELL")) 15 | (alistv (gensym "ALIST")) 16 | (value (gensym "VAL"))) 17 | (values (list alistv cell) 18 | (list alist `(assoc ,key ,alistv :test #'string-equal)) 19 | (list value) 20 | `(if ,cell 21 | (setf (cdr ,cell) ,value) 22 | (push (cons (alist-key ,key) ,value) ,alist)) 23 | `(pget ,key ,alistv)))) 24 | 25 | (defun remove-param (key alist) 26 | (remove key alist :key #'car :test #'string-equal)) 27 | 28 | (defun to-octets (thing &optional (external-format *external-format*)) 29 | (babel:string-to-octets thing :encoding external-format)) 30 | 31 | (defun from-octets (thing &optional (external-format *external-format*)) 32 | (babel:octets-to-string thing :encoding external-format)) 33 | 34 | (defun url-encode (thing &optional (external-format *external-format*)) 35 | (with-output-to-string (out) 36 | (loop for octet across (to-octets thing external-format) 37 | for char = (code-char octet) 38 | do (cond ((or (char<= #\0 char #\9) 39 | (char<= #\a char #\z) 40 | (char<= #\A char #\Z) 41 | (find char "-._~" :test #'char=)) 42 | (write-char char out)) 43 | (T (format out "%~2,'0x" (char-code char))))))) 44 | 45 | (defun url-decode (string &optional (external-format *external-format*)) 46 | (let ((out (make-array (length string) :element-type '(unsigned-byte 8) :fill-pointer 0))) 47 | (loop for i from 0 below (length string) 48 | for char = (aref string i) 49 | do (case char 50 | (#\% (vector-push (parse-integer string :start (+ i 1) :end (+ i 3) :radix 16) out) 51 | (incf i 2)) 52 | (#\+ (vector-push (char-code #\Space) out)) 53 | (T (vector-push (char-code char) out))) 54 | finally (return (babel:octets-to-string out external-format))))) 55 | 56 | (defgeneric sign (method data consumer-secret &optional token-secret) 57 | (:method (method data consumer-secret &optional token-secret) 58 | (error "Unknown signing method ~s." method)) 59 | (:method ((method (eql :plaintext)) data consumer-secret &optional token-secret) 60 | (format NIL "~a&~@[~a~]" (url-encode consumer-secret) (when token-secret (url-encode token-secret)))) 61 | (:method ((method (eql :hmac-sha1)) data consumer-secret &optional token-secret) 62 | (let* ((key (to-octets (sign :plaintext NIL consumer-secret token-secret))) 63 | (hmac (ironclad:make-hmac key :sha1))) 64 | (ironclad:update-hmac hmac (to-octets data)) 65 | (cl-base64:usb8-array-to-base64-string (ironclad:hmac-digest hmac)))) 66 | (:method ((method string) data consumer-secret &optional token-secret) 67 | (sign (find-symbol (string-upcase method) :keyword) data consumer-secret token-secret))) 68 | 69 | (defun make-nonce () 70 | (fuuid:to-string (fuuid:make-v4))) 71 | 72 | (defun make-timestamp () 73 | (write-to-string (- (get-universal-time) 74 | (encode-universal-time 0 0 0 1 1 1970 0)))) 75 | 76 | (defun param< (a b) 77 | (destructuring-bind (akey . aval) a 78 | (destructuring-bind (bkey . bval) b 79 | (let ((akey (alist-key akey)) 80 | (bkey (alist-key bkey))) 81 | (or (and (string= akey bkey) 82 | (string< aval bval)) 83 | (string< akey bkey)))))) 84 | 85 | (defun sort-params (params) 86 | (sort (copy-list params) #'param<)) 87 | 88 | (defun concat-params (params &key quote (delim "&")) 89 | (with-output-to-string (out) 90 | (loop for (pair . rest) on (sort-params params) 91 | for (key . val) = pair 92 | do (when (and key val) 93 | (format out (format NIL "~~a=~:[~~a~;~~s~]~~@[~a~~*~~]" quote delim) 94 | (url-encode (etypecase key 95 | (string key) 96 | (symbol (string-downcase key)))) 97 | (url-encode val) rest))))) 98 | 99 | (defun url-parts (url) 100 | (or (cl-ppcre:register-groups-bind (scheme host NIL port NIL path) 101 | ("^([a-zA-Z][a-zA-Z0-9+-.]+)://([a-zA-Z0-9-._:~%!$&'\\(\\)*+,;=]+?)(:([0-9]+))?(/+([^?#]*).*)?$" url) 102 | (list scheme host port (or path ""))) 103 | (error "Invalid URL ~s" url))) 104 | 105 | (defun normalize-url (url) 106 | (destructuring-bind (scheme host port path) (url-parts url) 107 | (format NIL "~(~a~)://~(~a~)~:[:~a~;~*~]/~a" 108 | scheme host (or (not port) 109 | (and (string= port "80") (string-equal scheme "HTTP")) 110 | (and (string= port "443") (string-equal scheme "HTTPS"))) port path))) 111 | 112 | (defun make-signature-base-string (method url params) 113 | (format NIL "~:@(~a~)&~a&~a" 114 | method (url-encode (normalize-url url)) (url-encode (concat-params params)))) 115 | 116 | (defun create-signature (consumer-secret token-secret method url oauth-params &optional params) 117 | (let ((oauth-params (remove-param :oauth_signature oauth-params))) 118 | (sign (pget :oauth_signature_method oauth-params) 119 | (make-signature-base-string method url (append oauth-params params)) 120 | consumer-secret token-secret))) 121 | 122 | (eval-when (:compile-toplevel :load-toplevel :execute) 123 | (defvar *whitespace* '(#\Space #\Linefeed #\Return #\Tab))) 124 | 125 | (defun start-p (start string) 126 | (and (<= (length start) (length string)) 127 | (string= start string :end2 (length start)))) 128 | 129 | (defun destructure-oauth-header (header) 130 | (unless (start-p "OAuth" header) 131 | (error "Header malformed, couldn't find \"OAuth\" prefix.")) 132 | (when (< (length "OAuth .") (length header)) 133 | (loop with buffer = (make-string-output-stream) 134 | with input = (make-string-input-stream header 6) 135 | with values = () 136 | with key = NIL 137 | with state = :expecting-key 138 | for c = (read-char input NIL NIL) while c 139 | do (labels ((write-to-buffer () 140 | (write-char c buffer)) 141 | (buffer-contents () 142 | (url-decode (get-output-stream-string buffer))) 143 | (change-state (new-state) 144 | (setf state new-state) 145 | (case state 146 | (:end-key (setf key (buffer-contents))) 147 | (:end-val (push (cons key (buffer-contents)) 148 | values)))) 149 | (change-state-immediate (new-state) 150 | (unread-char c input) (change-state new-state)) 151 | (parse-error () 152 | (error "Invalid character ~c in state ~s" c state))) 153 | (case state 154 | (:expecting-key 155 | (case c 156 | (#.*whitespace* NIL) 157 | ((#\, #\= #\") (parse-error)) 158 | (T (change-state-immediate :key)))) 159 | (:key 160 | (case c 161 | (#.*whitespace* (change-state :end-key)) 162 | ((#\=) (change-state-immediate :end-key)) 163 | ((#\, #\") (parse-error)) 164 | (T (write-to-buffer)))) 165 | (:end-key 166 | (case c 167 | (#.*whitespace* NIL) 168 | ((#\=) (change-state :expecting-val)) 169 | (T (parse-error)))) 170 | (:expecting-val 171 | (case c 172 | (#.*whitespace* NIL) 173 | ((#\") (change-state :val)) 174 | (T (parse-error)))) 175 | (:val 176 | (case c 177 | ((#\") (change-state :end-val)) 178 | ((#\, #\=) (parse-error)) 179 | (T (write-to-buffer)))) 180 | (:end-val 181 | (case c 182 | (#.*whitespace* NIL) 183 | ((#\,) (change-state :expecting-key)) 184 | (T (write-to-buffer)))))) 185 | finally (unless (eql state :end-val) 186 | (error "Unexpected end in state ~s" state)) 187 | (return (nreverse values))))) 188 | 189 | (defun oauth-response->alist (body) 190 | (mapcar (lambda (assignment) 191 | (let ((pair (cl-ppcre:split "=" assignment))) 192 | (cons (url-decode (first pair)) 193 | (url-decode (second pair))))) 194 | (cl-ppcre:split "&" body))) 195 | 196 | (defun alist->oauth-response (alist) 197 | (concat-params alist)) 198 | 199 | (defmethod user-agent ((default (eql T))) 200 | #.(format NIL "North oAuth client (~a ~a)" (machine-type) (software-type))) 201 | -------------------------------------------------------------------------------- /documentation.lisp: -------------------------------------------------------------------------------- 1 | (in-package #:org.shirakumo.north) 2 | 3 | ;; client.lisp 4 | (docs:define-docs 5 | (function call 6 | "Executes the given request object. 7 | 8 | If the http status code returned is 200, the response body is returned. 9 | Otherwise, an error of type REQUEST-FAILED is signalled. Note that the 10 | allowed extra arguments are dependant on the backend being used. The 11 | one parameter that must be universally recognised is the :FORM-DATA 12 | boolean, designating whether the parameters contain form data to be sent 13 | over the request such as files. Files in the parameter list should be 14 | either a cons of the parameter name and file as pathname or octet-vector, 15 | or a list of name, file, and optional keyword arguments. The only file 16 | keyword argument currently recognised is :CONTENT-TYPE, specifying the 17 | supplied content-type that should be submitted to the server for the file. 18 | 19 | See REQUEST 20 | See REQUEST-FAILED") 21 | 22 | (function call-signed 23 | "Execute the given request object after signing it. 24 | 25 | See REQUEST 26 | See CALL 27 | See MAKE-AUTHORIZED 28 | See MAKE-SIGNED") 29 | 30 | (function make-signed-request 31 | "Construct and execute a signed request for the given client. 32 | 33 | Returns the result of the request execution as the first value and the constructed 34 | request object itself as the second. 35 | 36 | See CLIENT 37 | See REQUEST 38 | See CALL-SIGNED") 39 | 40 | (function make-signed-data-request 41 | "Construct and execute a signed request to send data payloads. 42 | 43 | Each data value can be either a pathname or an octet-vector. 44 | Returns the result of the request execution as the first value and the constructed 45 | request object itself as the second. 46 | 47 | See CLIENT 48 | See REQUEST 49 | See CALL-SIGNED") 50 | 51 | (function initiate-authentication 52 | "Start the authentication process for the client. 53 | 54 | This performs an oauth/request-token request and constructs the proper 55 | oauth/authorize URL for the user to visit. This address is returned. 56 | If the provider did not confirm the callback, an error of type CALLBACK-UNCONFIRMED 57 | is signalled. 58 | This operation modifies the TOKEN and TOKEN-SECRET fields of the client. 59 | 60 | See CLIENT 61 | See MAKE-SIGNED-REQUEST") 62 | 63 | (function complete-authentication 64 | "Complete the authentication process for the client. 65 | 66 | This performs an oauth/access-token request. 67 | This operation modifies the TOKEN and TOKEN-SECRET fields of the client. 68 | When the client has a VERIFY-URI given, an additional oauth/verify request is made 69 | to test whether the full process was complete. 70 | 71 | See CLIENT 72 | See MAKE-SIGNED-REQUEST") 73 | 74 | (type client 75 | "An oAuth client class to encapsulate a single connection to a provider. 76 | 77 | Contains all the necessary information and state to manage the connection. 78 | You may serialise this object into a reloadable form using MAKE-LOAD-FORM. 79 | Unless the provider expires access tokens or the token is revoked for some reason 80 | or another, the authentication process for a client has to be done only once. 81 | 82 | See KEY 83 | See SECRET 84 | See TOKEN 85 | See TOKEN-SECRET 86 | See CALLBACK 87 | See REQUEST-TOKEN-URI 88 | See AUTHORIZE-URI 89 | See ACCESS-TOKEN-URI 90 | See VERIFY-URI 91 | See INITIATE-AUTHENTICATION 92 | See COMPLETE-AUTHENTICATION") 93 | 94 | (function key 95 | "Accesses the application key.") 96 | 97 | (function secret 98 | "Accesses the application secret.") 99 | 100 | (function token 101 | "Accesses the current (access or request) token.") 102 | 103 | (function token-secret 104 | "Accesses the current (access or request) token secret.") 105 | 106 | (function callback 107 | "Accesses the callback to which the oauth/authorize step should redirect. 108 | If it should not redirect, the callback must be exactly the string \"oob\".") 109 | 110 | (function request-token-uri 111 | "Accesses the oauth/request-token uri, the first endpoint for the oAuth process.") 112 | 113 | (function authorize-uri 114 | "Accesses the oauth/authorize uri, the second endpoint for the oAuth process.") 115 | 116 | (function access-token-uri 117 | "Accesses the oauth/access-token uri, the third endpoint for the oAuth process.") 118 | 119 | (function verify-uri 120 | "Accesses the oauth/verify uri, an optional endpoint to test whether the process completed successfully.")) 121 | 122 | ;; conditions.lisp 123 | (docs:define-docs 124 | (type north-condition 125 | "Base condition class for conditions in the North system. 126 | 127 | See REQUEST") 128 | 129 | (function request 130 | "The request object that the error occurred on. 131 | 132 | See NORTH-CONDITION") 133 | 134 | (type parameter-error 135 | "An error signalled when the parameters given to the provider are incomplete or badly specified. 136 | Should end up in an HTTP 400 return code. 137 | 138 | See NORTH-CONDITION") 139 | 140 | (type verification-error 141 | "An error signalled when the parameters given to the provider fail the verification test. 142 | Should end up in an HTTP 401 return code. 143 | 144 | See NORTH-CONDITION") 145 | 146 | (type client-error 147 | "An error signalled when the oAuth client encounters a problem. 148 | 149 | See NORTH-CONDITION") 150 | 151 | (type parameters-missing 152 | "An error signalled when the oAuth request does not include all the required oAuth parameters. 153 | 154 | See PARAMETERS 155 | See PARAMETER-ERROR") 156 | 157 | (function parameters 158 | "Accessor for the list of missing parameters that should have been supplied, but weren't. 159 | 160 | See PARAMETERS-MISSING") 161 | 162 | (type bad-version 163 | "An error signalled when the oAuth request specifies a bad version field. 164 | 165 | See PARAMETER-ERROR") 166 | 167 | (type verifier-taken 168 | "An error signalled when a verifier token for a request is re-used or the authorization step is repeated for the same request token. 169 | 170 | See VERIFICATION-ERROR") 171 | 172 | (type nonce-reused 173 | "An error signalled when a nonce is used twice within the same timestamp. 174 | 175 | See VERIFICATION-ERROR") 176 | 177 | (type invalid-signature 178 | "An error signalled when the oAuth signature cannot be verified. 179 | This is most likely due to a bad signing procedure on the client's behalf, or a 180 | disagreement about the tokens and secrets used in the signing process. 181 | 182 | See VERIFICATION-ERROR") 183 | 184 | (type invalid-verifier 185 | "An error signalled when the verifier of the request is invalid for the associated request token. 186 | 187 | See VERIFICATION-ERROR") 188 | 189 | (type invalid-token 190 | "An error signalled when the token of the request is invalid or unknown. 191 | 192 | See VERIFICATION-ERROR") 193 | 194 | (type invalid-application 195 | "An error signalled when the oauth_consumer_key of the request is invalid or unknown. 196 | 197 | See VERIFICATION-ERROR") 198 | 199 | (type request-failed 200 | "An error signalled when a client's request returned with a non-200 status code. 201 | 202 | See BODY 203 | See STATUS-CODE 204 | See HEADERS 205 | See CLIENT-ERROR") 206 | 207 | (function body 208 | "The returned http body of the failed request. This may be an octet-vector if the content type is not known to drakma. 209 | 210 | See REQUEST-FAILED 211 | See DRAKMA:*TEXT-CONTENT-TYPES*") 212 | 213 | (function status-code 214 | "The returned status code of the failed request. 215 | 216 | See REQUEST-FAILED") 217 | 218 | (function headers 219 | "The returned header of the failed request as an alist. 220 | 221 | See REQUEST-FAILED") 222 | 223 | (type callback-unconfirmed 224 | "An error signalled when the provider returns a non-\"true\" value for the oauth_callback_confirmed key. 225 | 226 | See CLIENT-ERROR")) 227 | 228 | ;; request.lisp 229 | (docs:define-docs 230 | (function make-signed 231 | "Modifies the request to add a signature to the oauth parameters. 232 | 233 | This will also modify the oauth parameters list to remove duplicates or empty values. 234 | Returns the request. 235 | 236 | See REQUEST 237 | See CREATE-SIGNATURE") 238 | 239 | (function make-authorized 240 | "Modifies the request to add the authorization header using the oauth parameters. 241 | 242 | If the oauth_signature parameter is missing, an error is signalled. 243 | Returns the request. 244 | 245 | See REQUEST") 246 | 247 | (function verify 248 | "Verifies whether the signature in the request is valid. 249 | To do this it destructures the Authorization header and uses it values to construct a 250 | new signature. This is then compared against the oauth_signature value in the oauth 251 | alist. 252 | 253 | See REQUEST 254 | See DESTRUCTURE-OAUTH-HEADER 255 | See CREATE-SIGNATURE") 256 | 257 | (type request 258 | "Container class to represent an HTTP request. 259 | 260 | Upon initialisation a few default oauth parameters are set, if not given already: 261 | oauth_nonce Set to (make-nonce) 262 | oauth_signature_method Set to \"HMAC-SHA1\" 263 | oauth_timestamp Set to (make-timestamp) 264 | oauth_version Set to \"1.0\" 265 | Additionally, if the headers given include an Authorization header, then the 266 | oauth parameters are overridden by the results of DESTRUCTURE-OAUTH-HEADER. 267 | 268 | See HTTP-METHOD 269 | See URL 270 | See PARAMETERS 271 | See HEADERS 272 | See OAUTH 273 | See DESTRUCTURE-OAUTH-HEADER") 274 | 275 | (function http-method 276 | "Accesses the HTTP-METHOD (GET/POST) of the request.") 277 | 278 | (function url 279 | "Accesses the URL of the request.") 280 | 281 | (function parameters 282 | "Accesses the parameters of the request.") 283 | 284 | (function headers 285 | "Accesses the HTTP headers of the request.") 286 | 287 | (function oauth 288 | "Accesses the pure oauth parameters of the request.") 289 | 290 | (function make-request 291 | "Shorthand function to construct a request object.")) 292 | 293 | ;; server.lisp 294 | (docs:define-docs 295 | (function make-application 296 | "Creates and adds a new application object to the server. 297 | 298 | Additionally supported keyword arguments are used as initargs for the application 299 | instance. Which application class is used depends on the server. 300 | 301 | See SERVER 302 | See APPLICATION") 303 | 304 | (function make-session 305 | "Creates and adds a new session object to the server. 306 | 307 | Additionally supported keyword arguments are used as initargs for the session 308 | instance. Which session class is used depends on the server. 309 | 310 | See SERVER 311 | See SESSION") 312 | 313 | (function application 314 | "Returns the application object associated with the given key on the server, if any. 315 | 316 | See SERVER 317 | See APPLICATION") 318 | 319 | (function session 320 | "Returns the session object associated with the given token on the server, if any. 321 | 322 | See SERVER 323 | See SESSION") 324 | 325 | (function rehash-session 326 | "Updates the session object to use a new token and token-secret. 327 | 328 | The server must ensure that the session will no longer be accessible through SESSION 329 | using the old token, but will be accessible through the newly generated token. 330 | 331 | See SERVER 332 | See SESSION") 333 | 334 | (function revoke-application 335 | "Removes the given application from the server. 336 | 337 | The application must be no longer reachable through CONSUMER on the server and all 338 | sessions authorized through this application must be invalidated. 339 | 340 | See SERVER 341 | See APPLICATION 342 | See SESSION") 343 | 344 | (function revoke-session 345 | "Removes the given session from the server. 346 | 347 | The session must no longer be valid and any client trying to use its tokens to 348 | access any resource or perform any step of the oauth process must be rejected. 349 | 350 | See SERVER 351 | See SESSION") 352 | 353 | (function record-nonce 354 | "Remember the given nonce for the used timestamp.") 355 | 356 | (function find-nonce 357 | "If the given nonce was used before on the given timestamp, return non-NIL.") 358 | 359 | (function oauth/request-token 360 | "Perform the oauth/request-token step of the process. 361 | 362 | This creates a new session object and returns four values: 363 | TOKEN --- The newly generated request token. 364 | TOKEN-SECRET --- The newly generated request token secret. 365 | CALLBACK-CONFIRMED --- Whether the callback has been confirmed. According to 366 | the spec, this should always be T. 367 | SESSION --- The newly created session object. 368 | 369 | Signals a PARAMETER-ERROR or VERIFICATION-ERROR on an invalid request. 370 | 371 | See SERVER 372 | See MAKE-SESSION 373 | See TOKEN 374 | See TOKEN-SECRET 375 | See SESSION") 376 | 377 | (function oauth/authorize 378 | "Perform the oauth/authorize step of the process. 379 | 380 | This verifies the request and returns four values: 381 | TOKEN --- The current request token. 382 | VERIFIER --- The (possibly newly generated) verifier for the next step. 383 | URL --- The callback URL to redirect to if the callback is not \"oob\". 384 | If the callback is indeed \"oob\", NIL is returned for this. 385 | SESSION --- The associated session object. 386 | 387 | Signals a VERIFICATION-ERROR on an invalid request. 388 | 389 | See SERVER 390 | See TOKEN 391 | See VERIFIER 392 | See CALLBACK 393 | See SESSION") 394 | 395 | (function oauth/access-token 396 | "Perform the oauth/access-token step of the process. 397 | 398 | This verifies the request and if successful, upgrades its access to full. 399 | It will also invalidate the session's verifier and rehash it. 400 | 401 | Returns three values: 402 | TOKEN --- The newly generated access token. 403 | TOKEN-SECRET --- The newly generated access token secret. 404 | SESSION --- The associated session object. 405 | 406 | Signals a PARAMETER-ERROR or VERIFICATION-ERROR on an invalid request. 407 | 408 | See SERVER 409 | See VERIFIER 410 | See REHASH-SESSION 411 | See TOKEN 412 | See TOKEN-SECRET 413 | See SESSION") 414 | 415 | (function oauth/verify 416 | "Standard endpoint to use on any protected resource. 417 | 418 | This verifies the request and makes sure it uses a valid access token. 419 | 420 | Returns two values: 421 | SESSION --- The associated session object. 422 | APPLICATION --- The associated application object. 423 | 424 | Signals a PARAMETER-ERROR or VERIFICATION-ERROR on an invalid request.") 425 | 426 | (type session 427 | "Session container to represent a connection with a consumer. 428 | 429 | See TOKEN 430 | See TOKEN-SECRET 431 | See VERIFIER 432 | See CALLBACK 433 | See KEY 434 | See ACCESS") 435 | 436 | (function verifier 437 | "Accessor to the verifier token that the consumer has to provide to exchange the request token for an access token. 438 | 439 | See SESSION") 440 | 441 | (function access 442 | "Accessor to what kind of access the session has. Should initially be :request, and is set to :access once the handshake has completed. 443 | 444 | See SESSION") 445 | 446 | (type application 447 | "Container for an OAuth application that provides consumers a means to connect to the provider. 448 | 449 | See KEY 450 | See SECRET 451 | See NAME") 452 | 453 | (function name 454 | "Accessor to the name of the application. 455 | 456 | See APPLICATION") 457 | 458 | (type server 459 | "Mixin representing a server class.") 460 | 461 | (type simple-server 462 | "A very primitive and simple sample server implementation that stores everything in mere hash tables. 463 | 464 | Do not use this server for your production provider. You should implement 465 | one yourself the provides proper persistence and expiration of the providers, 466 | sessions, and nonces. 467 | 468 | See SERVER 469 | See APPLICATIONS 470 | See SESSIONS 471 | See NONCES") 472 | 473 | (function applications 474 | "Accessor to the hash table of key -> application in the server. 475 | 476 | See SIMPLE-SERVER") 477 | 478 | (function sessions 479 | "Accessor to the hash table of token -> session in the server. 480 | 481 | See SIMPLE-SERVER") 482 | 483 | (function nonces 484 | "Accessor to the hash table of timestamp -> nonce-list in the server. 485 | 486 | See SIMPLE-SERVER")) 487 | 488 | ;; toolkit.lisp 489 | (docs:define-docs 490 | (variable *external-format* 491 | "The external format to use to url-en/decode and execute requests. 492 | Defaults to :UTF-8") 493 | 494 | (function alist-key 495 | "Turn THING into a string suitable for a property alist. 496 | 497 | Symbols get downcased, strings stay as they are, everything else results 498 | in a type error.") 499 | 500 | (function pget 501 | "Easy accessor for alists. 502 | 503 | Checks keys STRING-EQUALly and returns the value directly, if found. 504 | SETF-able. If a key is set that does not occur in the alist, a new 505 | entry is PUSHed, otherwise the existing one is modified. The key is 506 | coerced using ALIST-KEY 507 | 508 | See ALIST-KEY") 509 | 510 | (function remove-param 511 | "Remove the given key from the alist. 512 | 513 | Checks keys STRING-EQUALly and constructs a new alist.") 514 | 515 | (function url-encode 516 | "Encode the string into url-encoded format as required by the oAuth spec. 517 | 518 | Namely all characters except [0-9], [a-z], [A-Z], and [-._~] are encoded. 519 | 520 | See *EXTERNAL-FORMAT*") 521 | 522 | (function url-decode 523 | "Decode the string into plain text format. 524 | 525 | See *EXTERNAL-FORMAT*") 526 | 527 | (function sign 528 | "Signs the given data using the specified method. 529 | 530 | By default, :PLAINTEXT and :HMAC-SHA1 are supported. 531 | A string can be used for the method as well, but will be converted 532 | to a keyword first.") 533 | 534 | (function make-nonce 535 | "Creates a default nonce by turning a V4 UUID into a string.") 536 | 537 | (function make-timestamp 538 | "Creates a default timestamp by turning a unix timestamp into a string.") 539 | 540 | (function param< 541 | "Compares two parameters as required by the oauth spec. 542 | 543 | If the two keys of the parameters are equal, the values are compared instead. 544 | Comparison happens by lexicographic string<. If a key is a symbol it is coerced 545 | using ALIST-KEY before comparison.") 546 | 547 | (function sort-params 548 | "Creates a fresh list in which the parameters are sorted. 549 | 550 | See PARAM<") 551 | 552 | (function concat-params 553 | "Concatenate the given alist into a single parameter string. 554 | 555 | This intermits an equal sign between key and value, and DELIM between each pair. 556 | If QUOTE is non-NIL, the values are surrounded by #\\\".") 557 | 558 | (function url-parts 559 | "Splits the given URL into its parts of SCHEME, HOST, PORT, and PATH.") 560 | 561 | (function normalize-url 562 | "Normalises the URL to avoid ambiguity. 563 | 564 | In specific, it downcases the scheme, removes leading slashes from the path, and 565 | omits the 80 port if the scheme is HTTP, and the 443 port if the scheme is HTTPS.") 566 | 567 | (function make-signature-base-string 568 | "Creates a normalized signature base string as follows. 569 | 570 | TOKEN ::= METHOD '&' URL '&' PARAMS 571 | METHOD --- The method in uppercase 572 | URL --- The url-encoded and normalized URL. 573 | PARAMS --- The url-encoded and concatenated params. 574 | 575 | See URL-ENCODE 576 | See NORMALIZE-URL 577 | See CONCAT-PARAMS") 578 | 579 | (function create-signature 580 | "Create an oAuth signature for the given parameters. 581 | 582 | This does not include the oauth_signature parameters if it is passed in OAUTH-PARAMS. 583 | Calls SIGN using the oauth_signature_method oauth param, a normalized token from the 584 | OAUTH-PARAMS and GET-PARAMS, and the given CONSUMER-SECRET and TOKEN-SECRET. 585 | 586 | See SIGN 587 | See NORMALIZE-TOKEN") 588 | 589 | (function start-p 590 | "Returns T if START is found at the beginning of STRING.") 591 | 592 | (function destructure-oauth-header 593 | "Destructures an Authorization header into its oauth parameters. 594 | 595 | Returns an alist of all the parameters with their associated values. 596 | If the header is malformed, an error is signalled.") 597 | 598 | (function oauth-response->alist 599 | "Splits the body into an alist of keys and values.") 600 | 601 | (function alist->oauth-response 602 | "Concatenates an alist into an oauth response body. 603 | 604 | See CONCAT-PARAMS.")) 605 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | North

north

1.0.0

oAuth 1.0a server and client implementation, the successor to South.

About North

North is a library that implements the OAuth 1.0a consumer and provider protocols. It allows you to connect to an OAuth provider and request its resources, or to build your own OAuth provider.

How To: Client

If you want to connect to an OAuth provider, simply instantiate a client and pass it the required parameters. Following the Twitter requirements and using the tokens from Chirp, we end up with this:

(defvar *client*
  2 |   (make-instance
  3 |    'north:client
  4 |    :key "D1pMCK17gI10bQ6orBPS0w"
  5 |    :secret "BfkvKNRRMoBPkEtDYAAOPW4s2G9U8Z7u3KAf0dBUA"
  6 |    :request-token-uri "https://api.twitter.com/oauth/request_token"
  7 |    :authorize-uri "https://api.twitter.com/oauth/authorize"
  8 |    :access-token-uri "https://api.twitter.com/oauth/access_token"))
  9 | 

Next we start the authentication process:

(north:initiate-authentication *client*)
 10 | 

Visit the returned URL and enter the verification code:

(north:complete-authentication *client* ".....")
 11 | 

And finally we can access some resources:

(ql:quickload :cl-json)
 12 | (defmacro with-json-decoding (() &body body)
 13 |   `(let ((drakma:*text-content-types* (list* '("application" . "json") drakma:*text-content-types*)))
 14 |      (cl-json:decode-json-from-string
 15 |       (progn ,@body))))
 16 | 
 17 | (with-json-decoding ()
 18 |   (north:make-signed-request *client* "https://api.twitter.com/1.1/account/verify_credentials.json" :get
 19 |                              :params '(("include_entities" . "true"))))
 20 | 
 21 | (with-json-decoding ()
 22 |   (north:make-signed-request *client* "https://api.twitter.com/1.1/statuses/update.json" :post
 23 |                              :params '(("status" . "North and South, no matter where I look there's parens."))))
 24 | 

We can also post some data:

(with-json-decoding ()
 25 |   (north:make-signed-data-request *client* "https://api.twitter.com/1.1/statuses/update_with_media.json"
 26 |                                   `(("media[]" . #p"~/sweet-bear.jpg"))
 27 |                                   :params '(("status" . "Check out this sweet bear!"))))
 28 | 

In order to keep the access token and secret so you can resume your session without repeatedly logging in every time, you can serialise the client with make-load-form.

How To: Server

In order to provide a server you need to have a way of persisting two main pieces of information: applications, and sessions. The minimal amount of information necessary for an application is its key and secret. You most likely want to add additional information such as a name, icon, and description so that they can be displayed to the user when they authorise a consumer. A session needs to store quite a few pieces more: a token, token-secret, verifier token, callback, the application key, and the access rights. Default classes to contain all this information are provided through application and session.

However, the persistence and bookkeeping of these objects is still up to your server implementations. To do this, you should subclass server and implement methods for make-application, make-session, application, session, rehash-session, revoke-application, revoke-session, record-nonce, and find-nonce. What exactly these functions should accomplish is described in their docstrings and should be fairly obvious.

Once that is done, what's left to do is create a webservice with at least three endpoints. One for the request token, one for the authorization page, and one for the access token.

General Behaviour for All Endpoints

For each endpoint there exists a corresponding function to call, which will return a number of values that should be returned to the user. You can use alist->oauth-response to construct a properly formatted response body. The objects that you need to pass to these calls are your server instance and a request instance that encapsulates the data that the server received with the request.

Each endpoint function performs certain checks against the request and in case of problems signals an error. Your server should intercept these and act as follows: if the error is a parameter-error, the HTTP response code should be 400, and if the error is a verification-error, it should be 401. Any other error is up to you. You may display information about the error in the response body, but the exact formatting thereof is up to you as well. It is however a good idea to output the same data format as your other endpoints would, aside from the oauth specific ones.

The Request Token Endpoint

The request token endpoint should call oauth/request-token and return the values as oauth_token, oauth_token_secret, and oauth_callback_confirmed respectively.

The Authorize Endpoint

The authorize page is special in the sense that it is not called with an oauth signed request. Instead the user calls it through a browser and the only thing it receives is the request token. The provider must then first authenticate the user and after having done so, display a page to the user that shows information about the application that they're connecting through and give them the option to either allow the application access or deny it.

If the user selects allow, oauth/authorize should be called. If it returns a third value, the server should cause the user to be redirected to that URL. If not, the server should display a page that shows the second value, which is the verification token. The user must then copy this value into the consumer.

If the user selects deny, the provider must not necessarily do anything except ensure that the session is revoked and thus prevent the consumer from gaining access. It is not required to notify the consumer of this in any way.

The Access Token Endpoint

The access token endpoint should call oauth/access-token and return the values as oauth_token, and oauth_token_secret respectively.

Protected Resource Endpoint

Any such endpoint should call oauth/verify. This function returns nothing useful and only performs checks that if failed result in an error being signalled as usual. You may additionally want to check the session for permissions to access the specific endpoint if such a distinction exists. However, such additional functionality is up to you to design.

An Example Server / Client Setup

See the north-example system for a primitive, simple setup of a provider and consumer.

OAuth Overview

OAuth is supposed to provide a relatively convenient standardised way to authenticate a user against a service (provider), and then allow the application (consumer) to access resources on the user's behalf. Key to this are two parts, the signing process, and the actual authorisation process itself.

The Authorisation Process

Before an OAuth consumer can access any resources, it needs to obtain an access token. The path to this token happens in three distinct steps, during which several pieces of information need to be remembered and modified on both the consumer and provider's sides.

Step 0: Generating an Application

Unfortunately this is already where things begin to become awkward with the OAuth specification. The spec conflates what is essentially an application with the consumer. In order to improve clarity, North makes a distinction here. An application is a server-side instance that identifies a range of consumers. A consumer is a specific instance of a program that would like to connect to a provider through an application.

Whatever the case may be, before you can connect at all, you need to have access to an application key and secret. These two pieces are vital in the signing and requesting process. In North, this can be done through make-application. Once you have an application, you can get its key and secret.

Step 1: Requesting a Request Token

Now we actually start exchanging things with the provider as a consumer. To do this we send a signed request to the server's request-token endpoint. The request is signed using our application's secret, and we pass along the application's key as an OAuth parameter. Additionally we must give the provider a callback parameter that tells it what to do in the next step.

The provider then verifies our signature, and if it thinks everything is proper, then it sends back a body containing a request token and request secret. From here on out our requests will need to contain the token as an OAuth parameter and be signed with the request secret along with the application secret.

Step 2: The User Authorises the Consumer

The next step has to be done by the user themselves, such that granting access is always done through explicit consent. To do this, the consumer constructs an URL to the provider's authorize endpoint with the request token added as a GET parameter. The user then has to visit this URL.

Once on the page, the user should be confronted with a confirmation dialog, potentially also displaying all the things the consumer could get access to, if authorised. If the user accepts, the server generates a verifier token, which is then passed back to the consumer. This happens either automatically through a redirect, if the callback was a valid URL, or through the user manually, if the callback was the string "oob". In the latter case, the verifier is displayed to the user on the website, and they have to copy it into the consumer.

Step 3: Requesting an Access Token

The final step is to exchange the request token with an access token, using the verifier that was obtained in the previous section. To do this, a fully signed request is made to the provider's access-token endpoint, which verifies the request and verifier, rehashes the consumer's token, and upgrades its access. It then returns the newly generated access token and secret that will henceforth be used to sign requests.

Step 4: Accessing Resources

Now that we have an access token, we can start requesting whatever resources we were permitted to. A provider may for example offer several kinds of access tokens with varying ranges of permissions. The request endpoints are of course the provider's own decision. The only thing that OAuth specifies henceforth is that the request must be fully signed.

Error Handling

Now, you may notice in all this that there's no specification on how to handle errors. And you're right. The only thing OAuth says is that missing/duplicate parameters must result in an HTTP return code of 400, and an invalid signature must result in a return code of 401. Everything else, what specifically went wrong, how to fix it, or any other thing that might be useful is unspecified. Some providers do give some error information, but the format in which the error is presented is up to them.

As such, North makes no assumptions whatsoever about how to parse the body contents on an error. You will, unfortunately, have to deal with that on your own. To do this, handle the request-failed error and parse the body of it.

The Signing Process

Now comes the hard part. The actual signing process is unbelievably convoluted and incredibly easy to mess up. Let's start simple.

The Authorization Header

A signed request must contain an "Authorization" header, which must begin with the string "OAuth ". Following it are a series of oauth parameters. Each key/value pair is separated by a comma and a space, each key is separated from the value by an equals sign, and each value is surrounded by double-quotes. Each key and value must also be url-encoded. Keep in mind here that in order to transmit the request, this header is again url-encoded.

The OAuth Parameters

For each request a certain set of OAuth parameters must be present. Every request must have the oauth_consumer_key, oauth_signature_method, oauth_signature, oauth_timestamp, and oauth_nonce. Additional parameters are oauth_callback, oauth_token, and oauth_verifier, and whatever else the provider may want to require. These parameters must be ordered lexicographically by their keys and in case of duplicate keys by their values.

The oauth_consumer_key is the application key that we obtained in step 0. The oauth_timestamp must be an integer representing the unix-time when the request was formed. The oauth_nonce must be a unique string for each timestamp, but must not necessarily be globally unique.

The OAuth Signature

The final piece is the signature itself. The signature is created using a signing method (oauth_signature_method) over a signature base string, using a signature key.

The signature key is simply the application secret, appended with an &, appended with the token secret, if we have one.

The signature base string is constructed by the uppercase representation of the request method (POST/GET) followed by an &, followed by the url-encoded, normalised url, followed by an &, followed by the url-encoded, concatenated parameters.

Normalising the URL

The URL must be normalised such that the schema is in all lowercase and the port is omitted if it is 80 and the schema is http, or if it is 443 and the schema is https.

Concatenating the Parameters

The parameters here are all of the parameters. Namely the oauth parameters (except the signature itself), the get parameters, and the post parameters. The parameters must again be sorted as before, but now they are concatenated differently. Namely each pair is separated by an ampersand, the key is separated from the value by an equals sign, and both keys and values must be url-encoded. Note that unlike before, values must not be surrounded by double-quotes.

Signing the Token

Potentially any signature method you might want is supported. The provider can request whatever they want. North implements the plaintext, hmac-sha1, and cmac-aes methods suggested by the spec. See the relevant hashing methods for information on how they work.

A Reminder, Just For Fun

The request is sent, url-encoded. The authorization header has url-encoded parameter values. One of those is the signature, which was constructed from a base string that has an url-encoded url part, and an url-encoded parameter part. The parameter part has url-encoded parameter values. Url-encode!

System Information

1.0.0
Nicolas Hafner
Artistic

Definition Index

  • NORTH

    • ORG.SHIRAKUMO.NORTH
    Source
    No documentation provided.
    • EXTERNAL SPECIAL-VARIABLE

      *EXTERNAL-FORMAT*

          Source
          The external format to use to url-en/decode and execute requests.
           29 | Defaults to :UTF-8
        • EXTERNAL CLASS

          APPLICATION

              Source
              Container for an OAuth application that provides consumers a means to connect to the provider.
               30 | 
               31 | See KEY
               32 | See SECRET
               33 | See NAME
            • EXTERNAL CLASS

              CLIENT

                  Source
                  An oAuth client class to encapsulate a single connection to a provider.
                   34 | 
                   35 | Contains all the necessary information and state to manage the connection.
                   36 | You may serialise this object into a reloadable form using MAKE-LOAD-FORM.
                   37 | Unless the provider expires access tokens or the token is revoked for some reason
                   38 | or another, the authentication process for a client has to be done only once.
                   39 | 
                   40 | See KEY
                   41 | See SECRET
                   42 | See TOKEN
                   43 | See TOKEN-SECRET
                   44 | See CALLBACK
                   45 | See REQUEST-TOKEN-URI
                   46 | See AUTHORIZE-URI
                   47 | See ACCESS-TOKEN-URI
                   48 | See VERIFY-URI
                   49 | See INITIATE-AUTHENTICATION
                   50 | See COMPLETE-AUTHENTICATION
                • EXTERNAL CLASS

                  REQUEST

                      Source
                      Container class to represent an HTTP request.
                       51 | 
                       52 | Upon initialisation a few default oauth parameters are set, if not given already:
                       53 |   oauth_nonce             Set to (make-nonce)
                       54 |   oauth_signature_method  Set to "HMAC-SHA1"
                       55 |   oauth_timestamp         Set to (make-timestamp)
                       56 |   oauth_version           Set to "1.0"
                       57 | Additionally, if the headers given include an Authorization header, then the
                       58 | oauth parameters are overridden by the results of DESTRUCTURE-OAUTH-HEADER.
                       59 | 
                       60 | See HTTP-METHOD
                       61 | See URL
                       62 | See PARAMETERS
                       63 | See HEADERS
                       64 | See OAUTH
                       65 | See DESTRUCTURE-OAUTH-HEADER
                    • EXTERNAL CLASS

                      SERVER

                          Source
                          Mixin representing a server class.
                        • EXTERNAL CLASS

                          SIMPLE-SERVER

                              Source
                              A very primitive and simple sample server implementation that stores everything in mere hash tables.
                               73 | 
                               74 | Do not use this server for your production provider. You should implement
                               75 | one yourself the provides proper persistence and expiration of the providers,
                               76 | sessions, and nonces.
                               77 | 
                               78 | See SERVER
                               79 | See APPLICATIONS
                               80 | See SESSIONS
                               81 | See NONCES
                            • EXTERNAL CONDITION

                              INVALID-SIGNATURE

                                  Source
                                  An error signalled when the oAuth signature cannot be verified. 
                                   90 | This is most likely due to a bad signing procedure on the client's behalf, or a 
                                   91 | disagreement about the tokens and secrets used in the signing process.
                                   92 | 
                                   93 | See VERIFICATION-ERROR
                                • EXTERNAL CONDITION

                                  PARAMETER-ERROR

                                      Source
                                      An error signalled when the parameters given to the provider are incomplete or badly specified.
                                      102 | Should end up in an HTTP 400 return code.
                                      103 | 
                                      104 | See NORTH-CONDITION
                                    • EXTERNAL CONDITION

                                      VERIFICATION-ERROR

                                          Source
                                          An error signalled when the parameters given to the provider fail the verification test.
                                          113 | Should end up in an HTTP 401 return code.
                                          114 | 
                                          115 | See NORTH-CONDITION
                                        • EXTERNAL CONDITION

                                          VERIFIER-TAKEN

                                              Source
                                              An error signalled when a verifier token for a request is re-used or the authorization step is repeated for the same request token.
                                              116 | 
                                              117 | See VERIFICATION-ERROR
                                            • EXTERNAL FUNCTION

                                              ALIST->OAUTH-RESPONSE

                                                • ALIST
                                                Concatenates an alist into an oauth response body.
                                                118 | 
                                                119 | See CONCAT-PARAMS.
                                              • EXTERNAL FUNCTION

                                                CONCAT-PARAMS

                                                  • PARAMS
                                                  • &KEY
                                                  • QUOTE
                                                  • (DELIM &)
                                                  Source
                                                  Concatenate the given alist into a single parameter string.
                                                  120 | 
                                                  121 | This intermits an equal sign between key and value, and DELIM between each pair.
                                                  122 | If QUOTE is non-NIL, the values are surrounded by #\".
                                                • EXTERNAL FUNCTION

                                                  CREATE-SIGNATURE

                                                    • CONSUMER-SECRET
                                                    • TOKEN-SECRET
                                                    • METHOD
                                                    • URL
                                                    • OAUTH-PARAMS
                                                    • &OPTIONAL
                                                    • PARAMS
                                                    Source
                                                    Create an oAuth signature for the given parameters.
                                                    123 | 
                                                    124 | This does not include the oauth_signature parameters if it is passed in OAUTH-PARAMS.
                                                    125 | Calls SIGN using the oauth_signature_method oauth param, a normalized token from the
                                                    126 | OAUTH-PARAMS and GET-PARAMS, and the given CONSUMER-SECRET and TOKEN-SECRET.
                                                    127 | 
                                                    128 | See SIGN
                                                    129 | See NORMALIZE-TOKEN
                                                  • EXTERNAL FUNCTION

                                                    DESTRUCTURE-OAUTH-HEADER

                                                      • HEADER
                                                      Source
                                                      Destructures an Authorization header into its oauth parameters.
                                                      130 | 
                                                      131 | Returns an alist of all the parameters with their associated values.
                                                      132 | If the header is malformed, an error is signalled.
                                                    • EXTERNAL FUNCTION

                                                      MAKE-NONCE

                                                          Source
                                                          Creates a default nonce by turning a V4 UUID into a string.
                                                        • EXTERNAL FUNCTION

                                                          MAKE-REQUEST

                                                            • URL
                                                            • METHOD
                                                            • &KEY
                                                            • PARAMS
                                                            • HEADERS
                                                            • OAUTH
                                                            Source
                                                            Shorthand function to construct a request object.
                                                          • EXTERNAL FUNCTION

                                                            MAKE-TIMESTAMP

                                                                Source
                                                                Creates a default timestamp by turning a unix timestamp into a string.
                                                              • EXTERNAL FUNCTION

                                                                NORMALIZE-URL

                                                                  • URL
                                                                  Source
                                                                  Normalises the URL to avoid ambiguity. 
                                                                  133 | 
                                                                  134 | In specific, it downcases the scheme, removes leading slashes from the path, and
                                                                  135 | omits the 80 port if the scheme is HTTP, and the 443 port if the scheme is HTTPS.
                                                                • EXTERNAL FUNCTION

                                                                  PGET

                                                                    • KEY
                                                                    • ALIST
                                                                    Source
                                                                    Easy accessor for alists. 
                                                                    136 | 
                                                                    137 | Checks keys STRING-EQUALly and returns the value directly, if found.
                                                                    138 | SETF-able. If a key is set that does not occur in the alist, a new
                                                                    139 | entry is PUSHed, otherwise the existing one is modified. The key is
                                                                    140 | coerced using ALIST-KEY
                                                                    141 | 
                                                                    142 | See ALIST-KEY
                                                                  • EXTERNAL FUNCTION

                                                                    SORT-PARAMS

                                                                      • PARAMS
                                                                      Source
                                                                      Creates a fresh list in which the parameters are sorted.
                                                                      143 | 
                                                                      144 | See PARAM<
                                                                    • EXTERNAL FUNCTION

                                                                      URL-DECODE

                                                                        • STRING
                                                                        • &OPTIONAL
                                                                        • (EXTERNAL-FORMAT *EXTERNAL-FORMAT*)
                                                                        Source
                                                                        Decode the string into plain text format.
                                                                        145 | 
                                                                        146 | See *EXTERNAL-FORMAT*
                                                                      • EXTERNAL FUNCTION

                                                                        URL-ENCODE

                                                                          • THING
                                                                          • &OPTIONAL
                                                                          • (EXTERNAL-FORMAT *EXTERNAL-FORMAT*)
                                                                          Source
                                                                          Encode the string into url-encoded format as required by the oAuth spec.
                                                                          147 | 
                                                                          148 | Namely all characters except [0-9], [a-z], [A-Z], and [-._~] are encoded.
                                                                          149 | 
                                                                          150 | See *EXTERNAL-FORMAT*
                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                          ACCESS

                                                                            • OBJECT
                                                                            Accessor to what kind of access the session has. Should initially be :request, and is set to :access once the handshake has completed.
                                                                            151 | 
                                                                            152 | See SESSION
                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                            (SETF ACCESS)

                                                                              • NEW-VALUE
                                                                              • OBJECT
                                                                              No documentation provided.
                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                              ACCESS-TOKEN-URI

                                                                                • OBJECT
                                                                                Accesses the oauth/access-token uri, the third endpoint for the oAuth process.
                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                APPLICATION

                                                                                  • SERVER
                                                                                  • APPLICATION-KEY
                                                                                  Source
                                                                                  Returns the application object associated with the given key on the server, if any.
                                                                                  153 | 
                                                                                  154 | See SERVER
                                                                                  155 | See APPLICATION
                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                  APPLICATIONS

                                                                                    • OBJECT
                                                                                    Accessor to the hash table of key -> application in the server.
                                                                                    156 | 
                                                                                    157 | See SIMPLE-SERVER
                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                    AUTHORIZE-URI

                                                                                      • OBJECT
                                                                                      Accesses the oauth/authorize uri, the second endpoint for the oAuth process.
                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                      BODY

                                                                                        • CONDITION
                                                                                        The returned http body of the failed request. This may be an octet-vector if the content type is not known to drakma.
                                                                                        158 | 
                                                                                        159 | See REQUEST-FAILED
                                                                                        160 | See DRAKMA:*TEXT-CONTENT-TYPES*
                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                        CALL

                                                                                          • REQUEST
                                                                                          • &REST
                                                                                          • ARGS
                                                                                          Source
                                                                                          Executes the given request object.
                                                                                          161 | 
                                                                                          162 | If the http status code returned is 200, the response body is returned.
                                                                                          163 | Otherwise, an error of type REQUEST-FAILED is signalled. Note that the
                                                                                          164 | allowed extra arguments are dependant on the backend being used. The
                                                                                          165 | one parameter that must be universally recognised is the :FORM-DATA
                                                                                          166 | boolean, designating whether the parameters contain form data to be sent
                                                                                          167 | over the request such as files. Files in the parameter list should be
                                                                                          168 | either a cons of the parameter name and file as pathname or octet-vector,
                                                                                          169 | or a list of name, file, and optional keyword arguments. The only file
                                                                                          170 | keyword argument currently recognised is :CONTENT-TYPE, specifying the
                                                                                          171 | supplied content-type that should be submitted to the server for the file.
                                                                                          172 | 
                                                                                          173 | See REQUEST
                                                                                          174 | See REQUEST-FAILED
                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                          CALLBACK

                                                                                            • OBJECT
                                                                                            Accesses the callback to which the oauth/authorize step should redirect.
                                                                                            180 | If it should not redirect, the callback must be exactly the string "oob".
                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                            (SETF CALLBACK)

                                                                                              • NEW-VALUE
                                                                                              • OBJECT
                                                                                              No documentation provided.
                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                              COMPLETE-AUTHENTICATION

                                                                                                • CLIENT
                                                                                                • VERIFIER
                                                                                                • &OPTIONAL
                                                                                                • TOKEN
                                                                                                Source
                                                                                                Complete the authentication process for the client.
                                                                                                181 | 
                                                                                                182 | This performs an oauth/access-token request.
                                                                                                183 | This operation modifies the TOKEN and TOKEN-SECRET fields of the client.
                                                                                                184 | When the client has a VERIFY-URI given, an additional oauth/verify request is made
                                                                                                185 | to test whether the full process was complete.
                                                                                                186 | 
                                                                                                187 | See CLIENT
                                                                                                188 | See MAKE-SIGNED-REQUEST
                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                FIND-NONCE

                                                                                                  • SERVER
                                                                                                  • TIMESTAMP
                                                                                                  • NONCE
                                                                                                  Source
                                                                                                  If the given nonce was used before on the given timestamp, return non-NIL.
                                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                                  HEADERS

                                                                                                    • CONDITION
                                                                                                    Accesses the HTTP headers of the request.
                                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                                    (SETF HEADERS)

                                                                                                      • NEW-VALUE
                                                                                                      • OBJECT
                                                                                                      No documentation provided.
                                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                                      HTTP-METHOD

                                                                                                        • OBJECT
                                                                                                        Accesses the HTTP-METHOD (GET/POST) of the request.
                                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                                        (SETF HTTP-METHOD)

                                                                                                          • NEW-VALUE
                                                                                                          • OBJECT
                                                                                                          No documentation provided.
                                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                                          INITIATE-AUTHENTICATION

                                                                                                            • CLIENT
                                                                                                            Source
                                                                                                            Start the authentication process for the client.
                                                                                                            189 | 
                                                                                                            190 | This performs an oauth/request-token request and constructs the proper 
                                                                                                            191 | oauth/authorize URL for the user to visit. This address is returned.
                                                                                                            192 | If the provider did not confirm the callback, an error of type CALLBACK-UNCONFIRMED
                                                                                                            193 | is signalled.
                                                                                                            194 | This operation modifies the TOKEN and TOKEN-SECRET fields of the client.
                                                                                                            195 | 
                                                                                                            196 | See CLIENT
                                                                                                            197 | See MAKE-SIGNED-REQUEST
                                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                                            KEY

                                                                                                              • OBJECT
                                                                                                              Accesses the application key.
                                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                                              (SETF KEY)

                                                                                                                • NEW-VALUE
                                                                                                                • OBJECT
                                                                                                                No documentation provided.
                                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                                MAKE-APPLICATION

                                                                                                                  • SERVER
                                                                                                                  • &KEY
                                                                                                                  • NAME
                                                                                                                  • &ALLOW-OTHER-KEYS
                                                                                                                  Source
                                                                                                                  Creates and adds a new application object to the server.
                                                                                                                  198 | 
                                                                                                                  199 | Additionally supported keyword arguments are used as initargs for the application
                                                                                                                  200 | instance. Which application class is used depends on the server.
                                                                                                                  201 | 
                                                                                                                  202 | See SERVER
                                                                                                                  203 | See APPLICATION
                                                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                                                  MAKE-AUTHORIZED

                                                                                                                    • REQUEST
                                                                                                                    Source
                                                                                                                    Modifies the request to add the authorization header using the oauth parameters.
                                                                                                                    204 | 
                                                                                                                    205 | If the oauth_signature parameter is missing, an error is signalled.
                                                                                                                    206 | Returns the request.
                                                                                                                    207 | 
                                                                                                                    208 | See REQUEST
                                                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                                                    MAKE-SESSION

                                                                                                                      • SERVER
                                                                                                                      • APPLICATION
                                                                                                                      • CALLBACK
                                                                                                                      • &KEY
                                                                                                                      • ACCESS
                                                                                                                      • &ALLOW-OTHER-KEYS
                                                                                                                      Source
                                                                                                                      Creates and adds a new session object to the server.
                                                                                                                      209 | 
                                                                                                                      210 | Additionally supported keyword arguments are used as initargs for the session
                                                                                                                      211 | instance. Which session class is used depends on the server.
                                                                                                                      212 | 
                                                                                                                      213 | See SERVER
                                                                                                                      214 | See SESSION
                                                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                                                      MAKE-SIGNED

                                                                                                                        • REQUEST
                                                                                                                        • CONSUMER-SECRET
                                                                                                                        • &OPTIONAL
                                                                                                                        • TOKEN-SECRET
                                                                                                                        Source
                                                                                                                        Modifies the request to add a signature to the oauth parameters.
                                                                                                                        215 | 
                                                                                                                        216 | This will also modify the oauth parameters list to remove duplicates or empty values.
                                                                                                                        217 | Returns the request.
                                                                                                                        218 | 
                                                                                                                        219 | See REQUEST
                                                                                                                        220 | See CREATE-SIGNATURE
                                                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                                                        MAKE-SIGNED-DATA-REQUEST

                                                                                                                          • CLIENT
                                                                                                                          • URL
                                                                                                                          • DATA
                                                                                                                          • &KEY
                                                                                                                          • PARAMS
                                                                                                                          • HEADERS
                                                                                                                          • OAUTH
                                                                                                                          Source
                                                                                                                          Construct and execute a signed request to send data payloads.
                                                                                                                          221 | 
                                                                                                                          222 | Each data value can be either a pathname or an octet-vector.
                                                                                                                          223 | Returns the result of the request execution as the first value and the constructed
                                                                                                                          224 | request object itself as the second.
                                                                                                                          225 | 
                                                                                                                          226 | See CLIENT
                                                                                                                          227 | See REQUEST
                                                                                                                          228 | See CALL-SIGNED
                                                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                                                          MAKE-SIGNED-REQUEST

                                                                                                                            • CLIENT
                                                                                                                            • URL
                                                                                                                            • METHOD
                                                                                                                            • &KEY
                                                                                                                            • PARAMS
                                                                                                                            • HEADERS
                                                                                                                            • OAUTH
                                                                                                                            Source
                                                                                                                            Construct and execute a signed request for the given client.
                                                                                                                            229 | 
                                                                                                                            230 | Returns the result of the request execution as the first value and the constructed
                                                                                                                            231 | request object itself as the second.
                                                                                                                            232 | 
                                                                                                                            233 | See CLIENT
                                                                                                                            234 | See REQUEST
                                                                                                                            235 | See CALL-SIGNED
                                                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                                                            NAME

                                                                                                                              • OBJECT
                                                                                                                              Accessor to the name of the application.
                                                                                                                              236 | 
                                                                                                                              237 | See APPLICATION
                                                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                                                              (SETF NAME)

                                                                                                                                • NEW-VALUE
                                                                                                                                • OBJECT
                                                                                                                                No documentation provided.
                                                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                                                NONCES

                                                                                                                                  • OBJECT
                                                                                                                                  Accessor to the hash table of timestamp -> nonce-list in the server.
                                                                                                                                  238 | 
                                                                                                                                  239 | See SIMPLE-SERVER
                                                                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                                                                  (SETF NONCES)

                                                                                                                                    • NEW-VALUE
                                                                                                                                    • OBJECT
                                                                                                                                    No documentation provided.
                                                                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                                                                    OAUTH

                                                                                                                                      • OBJECT
                                                                                                                                      Accesses the pure oauth parameters of the request.
                                                                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                                                                      (SETF OAUTH)

                                                                                                                                        • NEW-VALUE
                                                                                                                                        • OBJECT
                                                                                                                                        No documentation provided.
                                                                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                                                                        OAUTH/ACCESS-TOKEN

                                                                                                                                          • SERVER
                                                                                                                                          • REQUEST
                                                                                                                                          Source
                                                                                                                                          Perform the oauth/access-token step of the process.
                                                                                                                                          240 | 
                                                                                                                                          241 | This verifies the request and if successful, upgrades its access to full.
                                                                                                                                          242 | It will also invalidate the session's verifier and rehash it.
                                                                                                                                          243 | 
                                                                                                                                          244 | Returns two values:
                                                                                                                                          245 |   TOKEN         --- The newly generated access token.
                                                                                                                                          246 |   TOKEN-SECRET  --- The newly generated access token secret.
                                                                                                                                          247 | 
                                                                                                                                          248 | Signals a PARAMETER-ERROR or VERIFICATION-ERROR on an invalid request.
                                                                                                                                          249 | 
                                                                                                                                          250 | See SERVER
                                                                                                                                          251 | See VERIFIER
                                                                                                                                          252 | See REHASH-SESSION
                                                                                                                                          253 | See TOKEN
                                                                                                                                          254 | See TOKEN-SECRET
                                                                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                                                                          OAUTH/AUTHORIZE

                                                                                                                                            • SERVER
                                                                                                                                            • REQUEST
                                                                                                                                            Source
                                                                                                                                            Perform the oauth/authorize step of the process.
                                                                                                                                            255 | 
                                                                                                                                            256 | This verifies the request and returns three values:
                                                                                                                                            257 |   TOKEN     --- The current request token.
                                                                                                                                            258 |   VERIFIER  --- The (possibly newly generated) verifier for the next step.
                                                                                                                                            259 |   URL       --- The callback URL to redirect to if the callback is not "oob".
                                                                                                                                            260 |                 If the callback is indeed "oob", NIL is returned for this.
                                                                                                                                            261 | 
                                                                                                                                            262 | Signals a VERIFICATION-ERROR on an invalid request.
                                                                                                                                            263 | 
                                                                                                                                            264 | See SERVER
                                                                                                                                            265 | See TOKEN
                                                                                                                                            266 | See VERIFIER
                                                                                                                                            267 | See CALLBACK
                                                                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                                                                            OAUTH/REQUEST-TOKEN

                                                                                                                                              • SERVER
                                                                                                                                              • REQUEST
                                                                                                                                              Source
                                                                                                                                              Perform the oauth/request-token step of the process.
                                                                                                                                              268 | 
                                                                                                                                              269 | This creates a new session object and returns three values:
                                                                                                                                              270 |   TOKEN               --- The newly generated request token.
                                                                                                                                              271 |   TOKEN-SECRET        --- The newly generated request token secret.
                                                                                                                                              272 |   CALLBACK-CONFIRMED  --- Whether the callback has been confirmed. According to
                                                                                                                                              273 |                           the spec, this should always be T.
                                                                                                                                              274 | 
                                                                                                                                              275 | Signals a PARAMETER-ERROR or VERIFICATION-ERROR on an invalid request.
                                                                                                                                              276 | 
                                                                                                                                              277 | See SERVER
                                                                                                                                              278 | See MAKE-SESSION
                                                                                                                                              279 | See TOKEN
                                                                                                                                              280 | See TOKEN-SECRET
                                                                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                                                                              OAUTH/VERIFY

                                                                                                                                                • SERVER
                                                                                                                                                • REQUEST
                                                                                                                                                Source
                                                                                                                                                Standard endpoint to use on any protected resource.
                                                                                                                                                281 | 
                                                                                                                                                282 | This verifies the request and makes sure it uses a valid access token.
                                                                                                                                                283 | Returns T on success.
                                                                                                                                                284 | 
                                                                                                                                                285 | Signals a PARAMETER-ERROR or VERIFICATION-ERROR on an invalid request.
                                                                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                PARAMETERS

                                                                                                                                                  • CONDITION
                                                                                                                                                  Accesses the parameters of the request.
                                                                                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                  (SETF PARAMETERS)

                                                                                                                                                    • NEW-VALUE
                                                                                                                                                    • OBJECT
                                                                                                                                                    No documentation provided.
                                                                                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                    RECORD-NONCE

                                                                                                                                                      • SERVER
                                                                                                                                                      • TIMESTAMP
                                                                                                                                                      • NONCE
                                                                                                                                                      Source
                                                                                                                                                      Remember the given nonce for the used timestamp.
                                                                                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                      REHASH-SESSION

                                                                                                                                                        • SERVER
                                                                                                                                                        • SESSION
                                                                                                                                                        Source
                                                                                                                                                        Updates the session object to use a new token and token-secret.
                                                                                                                                                        286 | 
                                                                                                                                                        287 | The server must ensure that the session will no longer be accessible through SESSION
                                                                                                                                                        288 | using the old token, but will be accessible through the newly generated token.
                                                                                                                                                        289 | 
                                                                                                                                                        290 | See SERVER
                                                                                                                                                        291 | See SESSION
                                                                                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                        REQUEST

                                                                                                                                                          • CONDITION
                                                                                                                                                          The request object that the error occurred on.
                                                                                                                                                          292 | 
                                                                                                                                                          293 | See NORTH-CONDITION
                                                                                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                          REQUEST-TOKEN-URI

                                                                                                                                                            • OBJECT
                                                                                                                                                            Accesses the oauth/request-token uri, the first endpoint for the oAuth process.
                                                                                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                            REVOKE-APPLICATION

                                                                                                                                                              • SERVER
                                                                                                                                                              • APPLICATION-KEY
                                                                                                                                                              Source
                                                                                                                                                              Removes the given application from the server.
                                                                                                                                                              294 | 
                                                                                                                                                              295 | The application must be no longer reachable through CONSUMER on the server and all
                                                                                                                                                              296 | sessions authorized through this application must be invalidated.
                                                                                                                                                              297 | 
                                                                                                                                                              298 | See SERVER
                                                                                                                                                              299 | See APPLICATION
                                                                                                                                                              300 | See SESSION
                                                                                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                              REVOKE-SESSION

                                                                                                                                                                • SERVER
                                                                                                                                                                • TOKEN
                                                                                                                                                                Source
                                                                                                                                                                Removes the given session from the server.
                                                                                                                                                                301 | 
                                                                                                                                                                302 | The session must no longer be valid and any client trying to use its tokens to
                                                                                                                                                                303 | access any resource or perform any step of the oauth process must be rejected.
                                                                                                                                                                304 | 
                                                                                                                                                                305 | See SERVER
                                                                                                                                                                306 | See SESSION
                                                                                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                SECRET

                                                                                                                                                                  • OBJECT
                                                                                                                                                                  Accesses the application secret.
                                                                                                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                  (SETF SECRET)

                                                                                                                                                                    • NEW-VALUE
                                                                                                                                                                    • OBJECT
                                                                                                                                                                    No documentation provided.
                                                                                                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                    SESSION

                                                                                                                                                                      • SERVER
                                                                                                                                                                      • TOKEN
                                                                                                                                                                      Source
                                                                                                                                                                      Returns the session object associated with the given token on the server, if any.
                                                                                                                                                                      307 | 
                                                                                                                                                                      308 | See SERVER
                                                                                                                                                                      309 | See SESSION
                                                                                                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                      SESSIONS

                                                                                                                                                                        • OBJECT
                                                                                                                                                                        Accessor to the hash table of token -> session in the server.
                                                                                                                                                                        310 | 
                                                                                                                                                                        311 | See SIMPLE-SERVER
                                                                                                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                        (SETF SESSIONS)

                                                                                                                                                                          • NEW-VALUE
                                                                                                                                                                          • OBJECT
                                                                                                                                                                          No documentation provided.
                                                                                                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                          SIGN

                                                                                                                                                                            • METHOD
                                                                                                                                                                            • DATA
                                                                                                                                                                            • CONSUMER-SECRET
                                                                                                                                                                            • &OPTIONAL
                                                                                                                                                                            • TOKEN-SECRET
                                                                                                                                                                            Source
                                                                                                                                                                            Signs the given data using the specified method.
                                                                                                                                                                            312 | 
                                                                                                                                                                            313 | By default, :PLAINTEXT, :HMAC-SHA1, and :CMAC-AES are supported.
                                                                                                                                                                            314 | A string can be used for the method as well, but will be converted
                                                                                                                                                                            315 | to a keyword first.
                                                                                                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                            STATUS-CODE

                                                                                                                                                                              • CONDITION
                                                                                                                                                                              The returned status code of the failed request.
                                                                                                                                                                              316 | 
                                                                                                                                                                              317 | See REQUEST-FAILED
                                                                                                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                              TOKEN

                                                                                                                                                                                • OBJECT
                                                                                                                                                                                Accesses the current (access or request) token.
                                                                                                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                (SETF TOKEN)

                                                                                                                                                                                  • NEW-VALUE
                                                                                                                                                                                  • OBJECT
                                                                                                                                                                                  No documentation provided.
                                                                                                                                                                                • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                  TOKEN-SECRET

                                                                                                                                                                                    • OBJECT
                                                                                                                                                                                    Accesses the current (access or request) token secret.
                                                                                                                                                                                  • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                    URL

                                                                                                                                                                                      • OBJECT
                                                                                                                                                                                      Accesses the URL of the request.
                                                                                                                                                                                    • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                      (SETF URL)

                                                                                                                                                                                        • NEW-VALUE
                                                                                                                                                                                        • OBJECT
                                                                                                                                                                                        No documentation provided.
                                                                                                                                                                                      • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                        VERIFIER

                                                                                                                                                                                          • OBJECT
                                                                                                                                                                                          Accessor to the verifier token that the consumer has to provide to exchange the request token for an access token.
                                                                                                                                                                                          318 | 
                                                                                                                                                                                          319 | See SESSION
                                                                                                                                                                                        • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                          (SETF VERIFIER)

                                                                                                                                                                                            • NEW-VALUE
                                                                                                                                                                                            • OBJECT
                                                                                                                                                                                            No documentation provided.
                                                                                                                                                                                          • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                            VERIFY

                                                                                                                                                                                              • REQUEST
                                                                                                                                                                                              • CONSUMER-SECRET
                                                                                                                                                                                              • &OPTIONAL
                                                                                                                                                                                              • TOKEN-SECRET
                                                                                                                                                                                              Source
                                                                                                                                                                                              Verifies whether the signature in the request is valid.
                                                                                                                                                                                              320 | To do this it destructures the Authorization header and uses it values to construct a 
                                                                                                                                                                                              321 | new signature. This is then compared against the oauth_signature value in the oauth
                                                                                                                                                                                              322 | alist.
                                                                                                                                                                                              323 | 
                                                                                                                                                                                              324 | See REQUEST
                                                                                                                                                                                              325 | See DESTRUCTURE-OAUTH-HEADER
                                                                                                                                                                                              326 | See CREATE-SIGNATURE
                                                                                                                                                                                            • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                              VERIFY-URI

                                                                                                                                                                                                • OBJECT
                                                                                                                                                                                                Accesses the oauth/verify uri, an optional endpoint to test whether the process completed successfully.
                                                                                                                                                                                              • EXTERNAL GENERIC-FUNCTION

                                                                                                                                                                                                (SETF VERIFY-URI)

                                                                                                                                                                                                  • NEW-VALUE
                                                                                                                                                                                                  • OBJECT
                                                                                                                                                                                                  No documentation provided.
                                                                                                                                                                                                • EXTERNAL SETF-EXPANDER

                                                                                                                                                                                                  PGET

                                                                                                                                                                                                    • KEY
                                                                                                                                                                                                    • ALIST
                                                                                                                                                                                                    Source
                                                                                                                                                                                                    No documentation provided.
                                                                                                                                                                                                --------------------------------------------------------------------------------