Welcome to the CHICKEN Scheme pasting service

dynamic scoping woes added by andyjpb on Mon Oct 29 20:05:07 2012

(use uri-common http-client intarweb)
(use data-structures)

;export make-oauth-service-provider
;       make-oauth-service
;       authenticated-call-with-input-request
;       with-oauth
;       acquire-temporary-credential
;       authorize-resource-owner
;       acquire-token-credential


(define empty-credential  '("" . ""))

(define (make-oauth-credential identifier secret)
 (cons identifier secret))

(define token car)
(define secret cdr)

(define supported-signatures   '(plaintext)) ; '(plaintext hmac-sha1 rsa-sha1)
(define supported-methods      '(POST)) ; '(POST GET)
(define supported-transmission '(authorization-header)) ; '(authorization-header request-entity-body query-string)

; Differences between 1.0 and 1.0a
;   http://code.google.com/p/oauth/source/diff?spec=svn1058&old=991&r=1058&format=unidiff&path=%2Fspec%2Fcore%2F1.0a%2Foauth-core-1_0a.xml
;     + oauth_callback in temporary credential acquisition rather than owner auth
;     + oauth_callback_confirmed in temporary credential response
;     + presence of oauth_verifier in owner auth callback query string
;     + in absence of callback, display of verification code rather than assertion that auth has completed.
; Differences between 1.0a and rfc5849
;   http://tools.ietf.org/html/rfc5849#appendix-A
;     + MUST use TLS/SSL with PLAINTEXT
;     + nonce and timestamp parameters OPTIONAL when using PLAINTEXT
;     + permitted omitting empty oauth_token
;     + various other things only relevant for non PLAINTEXT signatures
(define supported-versions     '(1.0 1.0a rfc5849))

(define (memv? obj lst) (and (memv obj lst) #t))


; Particulars specified by RFC5849, Section 2
(define (make-oauth-service-provider #!key protocol-version signature-method owner-auth-url
				     credential-request-url (credential-request-method 'POST)
				     token-request-url (token-request-method 'POST)
				     (transmission-method 'authorization-header))
  (assert (memv protocol-version supported-versions))
  (assert (string? credential-request-url))
  (assert (string? owner-auth-url))
  (assert (string? token-request-url))
  (assert (memv signature-method supported-signatures))
  (assert (memv credential-request-method supported-methods))
  (assert (memv token-request-method supported-methods))
  (assert (memv transmission-method supported-transmission))

  (let ((credential-request-url (uri-reference credential-request-url))
	(owner-auth-url         (uri-reference owner-auth-url))
	(token-request-url      (uri-reference token-request-url)))
    (assert (eqv? 'https (uri-scheme credential-request-url)))
    (assert (eqv? 'https (uri-scheme token-request-url)))

    `((protocol-version       . ,protocol-version)
      (signature-method       . ,signature-method)
      (credential-request-req . ,(make-request uri:    credential-request-url
					       method: credential-request-method))
      (owner-auth-url         . ,owner-auth-url)
      (token-request-req      . ,(make-request uri:    token-request-url
					       method: token-request-method))
      (confirms-callback      . ,(memv? protocol-version '(1.0a rfc5849)))
      (callback-on-credential . ,(memv? protocol-version '(1.0a rfc5849)))
      (callback-on-owner-auth . ,(memv? protocol-version '(1.0)))
      (sends-oauth-verifier   . ,(memv? protocol-version '(1.0a rfc5849)))
      )))


(define (make-oauth-service #!key service client-credential)
 (alist-cons 'client-credential client-credential service))


(define (authenticated-call-with-input-request service protocol-parameters token-credential uri-or-request writer reader)
  (let* ((uri (cond ((uri? uri-or-request) uri-or-request) ; stolen from http-client
		    ((string? uri-or-request) (uri-reference uri-or-request))
		    (else (request-uri uri-or-request))))
	 (req (if (request? uri-or-request) ; stolen from http-client
		uri-or-request
		(make-request uri: uri method: (if writer 'POST 'GET))))
	 (body (if (list? writer) writer #f)) ; RFC5849: Section 3.4.1.3.1. Parameter Sources
	 (signature-method (alist-ref 'signature-method service))
	 (protocol-parameters
	   (append protocol-parameters
		   `((oauth_consumer_key     . ,(token (alist-ref 'client-credential service)))
		     (oauth_token            . ,(token token-credential))
		     (oauth_signature_method . ,(string-upcase (symbol->string signature-method)))
		     (oauth_version          . ,(exact->inexact (alist-ref 'protocol-version service)))
		     ,@(if (not (eqv? 'plaintext signature-method))
			 `((oauth_timestamp . "bar")
			   (oauth_nonce . "bar"))
			 '()))))
	 (signature (sign-request req protocol-parameters body service token-credential))
	 (protocol-parameters (alist-cons 'oauth_signature signature protocol-parameters)))
    (call-with-input-request
      (update-request
	req
	headers: (headers `((authorization . (#(OAuth ,protocol-parameters))))))
      (or body writer)
      reader)))


(define %encode uri-encode-string)


(define (sign-request request protocol-parameters body service token-credential)
  ; For hmac-sha1 and rsa-sha1 we must assert the http request entity-body conditions of RFC5849: Section 3.4.1.3.1
  (let ((signature-method (alist-ref 'signature-method service)))
    (case signature-method
      ((plaintext) ; RFC5849: Section 3.4.4 - MUST be used with TLS/SSL.
       ;(assert (eqv? 'https (uri-scheme (request-uri request))))
       (let ((client-credential (alist-ref 'client-credential service)))
	 (conc (%encode (secret client-credential)) "&" (%encode (secret token-credential)))))
      (else (abort (conc signature-method " signature method not implemented!"))))))


; This generates an "Authorization: Oauth ..." header. The OAuth spec calls for
; "Authorization: OAuth ..." but HTTP declares it as case-insensitive:
;
; "HTTP provides a simple challenge-response authentication mechanism that MAY
; be used by a server to challenge a client request and by a client to provide
; authentication information. It uses an extensible, case-insensitive token to
; identify the authentication scheme, followed by a comma-separated list of
; attribute-value pairs which carry the parameters necessary for achieving
; authentication via that scheme."
;  -- RFC2617, section 1.2
;
(authorization-param-subunparsers
  (alist-cons
    'OAuth
    (lambda (params)
      (string-intersperse
	(map (lambda (p)
	       (conc  (%encode (symbol->string (car p))) "=\"" (%encode (->string (cdr p))) "\""))
	     params)
      ", "))
    (authorization-param-subunparsers)))


(define (with-oauth service token-credential thunk)
  (fluid-let ((call-with-input-request
		(cut authenticated-call-with-input-request service '() token-credential <> <> <>)))
	      (thunk)))


(define (acquire-temporary-credential service #!optional callback-url)
  (let* ((callback-on-credential (alist-ref 'callback-on-credential service))
	 (_ (if (and (not callback-on-credential) callback-url)
	     (abort "This service does not accept callback-url during temporary credential acquisition.")))
	 (resp
	   (nth-value 0 (authenticated-call-with-input-request
			  service
			  `(,@(if (and callback-on-credential callback-url)
				`((oauth_callback . ,callback-url))
				'()))
			  empty-credential
			  (alist-ref 'credential-request-req service)
			  "" ; forces POST even if credential-request-req isn't a request object.
			  (lambda (p) (form-urldecode (read-string #f p)))))) ; string that stray \r ???
	 (credential (make-oauth-credential
		       (alist-ref 'oauth_token resp eqv? "")
		       (alist-ref 'oauth_token_secret resp eqv? "")))
	 (_ (if (alist-ref 'confirms-callback service)
	      (assert (alist-ref 'oauth_callback_confirmed resp)))) ; RFC5849: Section 2.1
	 (rest (remove
		 (lambda (e) (memv (car e) '(oauth_token_secret oauth_token oauth_callback_confirmed)))
		 resp)))
    (values credential rest)))

(define (authorize-resource-owner service temporary-credential #!optional callback-url)
  (let* ((callback-on-owner-auth (alist-ref 'callback-on-owner-auth service))
	 (_ (if (and (not callback-on-owner-auth) callback-url)
	      (abort "This service does not accept callback-url during resource owner authorization.")))
	 (callback-url (cond ((uri? callback-url) (uri->string callback-url))
			     (else callback-url)))
	 (uri (alist-ref 'owner-auth-url service))
	 (query (uri-query uri))
	 (query (alist-cons 'oauth_token (token temporary-credential) query))
	 (query (if (and callback-on-owner-auth callback-url)
		  (alist-cons 'oauth_callback callback-url query)
		  query))
	 (uri (update-uri uri query: query)))
  uri)) ; return a uri object that the user can be redirected to.

(define (acquire-token-credential service temporary-credential #!optional verifier)
  (let* ((_ (if (alist-ref 'sends-oauth-verifier service) (abort "oauth_verifier MUST be supplied!")))
	 (resp (nth-value 0 (authenticated-call-with-input-request
			      service
			      (if verifier `((oauth_verifier . ,verifier)) '())
			      temporary-credential
			      (alist-ref 'token-request-req service)
			      ""; forces POST even if token-request-req isn't a request object.
			      (lambda (p) (form-urldecode (read-string #f p)))))) ; string that stray \r ???
	 (credential (make-oauth-credential
		       (alist-ref 'oauth_token resp eqv? "")
		       (alist-ref 'oauth_token_secret resp eqv? "")))
	 (rest (remove
		 (lambda (e) (memv (car e) '(oauth_token_secret oauth_token)))
		 resp)))
    (values credential rest)))




(define dropbox (make-oauth-service-provider
		  protocol-version: 1.0
		  credential-request-url: "https://api.dropbox.com/1/oauth/request_token"
		  owner-auth-url: "https://www.dropbox.com/1/oauth/authorize"
		  token-request-url: "https://api.dropbox.com/1/oauth/access_token"
		  signature-method: 'plaintext))
;(define mydropbox (make-oauth-service service: dropbox client-credential: (make-oauth-credential "p" "q")))
;  (with-oauth mydropbox '("a" . "b") (lambda () (call-with-input-request "http://www.google.com" #f (cut read-string #f <>))

Your annotation:

Enter a new annotation:

Your nick:
The title of your paste:
Your paste (mandatory) :
Which operator can be represented by an apostrophe?
Visually impaired? Let me spell it for you (wav file) download WAV