Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- xquery version "3.1";
- module namespace interceptor="http://haptixgames.com/oauth-interceptor";
- import module namespace functx="http://www.functx.com";
- import module namespace xqjson="http://xqilla.sourceforge.net/lib/xqjson";
- import module namespace console="http://exist-db.org/xquery/console";
- declare namespace http="http://expath.org/ns/http-client";
- declare namespace json="http://www.json.org";
- declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
- declare variable $interceptor:DATA-PATH := '/db/apps/OAuthInterceptor/data';
- declare function interceptor:authorizationHeader($state)
- {
- let $pairs := interceptor:registration-document($state)/request/json
- let $auth-option :=
- try {
- xs:boolean($pairs/pair[@name eq "o"]/item[pair/@name eq "k" and pair eq "u_tba"]/pair[@name eq "v"]/string())
- } catch * {
- false()
- }
- return if($auth-option) then
- (
- element header { attribute name { "Authorization" },
- attribute value {
- "Basic "||util:base64-encode($pairs/pair[@name eq "i"]||":"||$pairs/pair[@name eq "k"])
- }
- }
- )
- else ()
- };
- (:
- declare
- %rest:OPTIONS
- function interceptor:register-state-options()
- {
- <rest:response>
- <http:response status="200">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- <http:header name="Access-Control-Allow-Methods" value="POST"/>
- </http:response>
- </rest:response>
- };
- :)
- declare
- %rest:POST("{$payload}")
- %rest:path("/oauth-interceptor/register-state")
- %rest:consumes("multipart/form-data")
- %rest:produces("application/json")
- %output:media-type("application/json")
- %output:method("json")
- function interceptor:register-state($payload as xs:string)
- {
- try
- {
- (:let $json := util:base64-decode($payload):)
- let $xml := xqjson:parse-json($payload)
- let $location :=
- xmldb:store(
- $interceptor:DATA-PATH,
- $xml/pair[@name='s']||'.reg',
- element registration {
- attribute state { $xml/pair[@name='s'] },
- element request {
- attribute timeStamp { util:system-dateTime() },
- $xml
- }
- },
- 'application/xml')
- return
- (<rest:response>
- <http:response status="200">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>)
- }
- catch *
- {
- <rest:response>
- <http:response status="500">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>
- }
- };
- declare
- %rest:GET
- %rest:path("/oauth-interceptor")
- %rest:query-param("error", "{$error}")
- %rest:query-param("state", "{$state}")
- %rest:query-param("code", "{$code}")
- function interceptor:callback($error, $state, $code)
- {
- (
- if(not(interceptor:registration-exists($state))) then
- (
- interceptor:insert-error($state,"registration-document-not-found","Unable to find registration record.")
- )
- else if(empty($error)) then
- (
- (: TODO - determine post type u_frm/u_qry :)
- let $body := interceptor:build-token-post-from-auth-code($code,$state)
- let $response := httpclient:post(
- xs:anyURI(interceptor:registration-document($state)/request/json/pair[@name eq "t_u"]),
- $body,
- false(),
- element headers {
- element header { attribute name { 'Content-Type' }, attribute value { 'application/x-www-form-urlencoded' } },
- interceptor:authorizationHeader($state)
- })
- (: TODO - set content type on u_cnt :)
- (: TODO - pick apart response and determine errors :)
- return
- if(number($response/@statusCode) ne 200) then
- (
- interceptor:insert-error($state,number($response/@statusCode),"Faulted HTTP response while obtaining access token."),
- interceptor:insert-error($state, try{xqjson:parse-json(util:base64-decode($response/httpclient:body/text()))}catch*{()},"Transport failure while obtaining access token.")
- )
- else
- (
- update insert element
- response {
- attribute timeStamp { util:system-dateTime() },
- xqjson:parse-json(util:base64-decode($response/httpclient:body/text()))
- } into
- interceptor:registration-document($state)
- )
- )
- else
- (
- interceptor:insert-error($state,$error,"Failure while obtaining authorization code.")
- ),
- interceptor:clear-secrets($state),
- interceptor:redirection-response($state)
- )
- };
- declare function interceptor:insert-error($state,$error,$description)
- {
- if(interceptor:registration-exists($state)) then
- (
- update insert element
- error {
- attribute timeStamp { util:system-dateTime() },
- element short { $error },
- element long { $description }
- } into
- interceptor:registration-document($state)
- )
- else ()
- };
- declare function interceptor:build-token-post-from-auth-code($code,$state)
- {
- if(interceptor:registration-exists($state)) then
- (
- let $pairs := interceptor:registration-document($state)/request/json
- let $token-request :=
- util:unescape-uri(
- "code=" || $code || "&" ||
- "client_id=" || $pairs/pair[@name eq "i"] || "&" ||
- "client_secret=" || $pairs/pair[@name eq "k"] || "&" ||
- "redirect_uri=" || $pairs/pair[@name eq "r_u"] || "&" ||
- string-join(for $field in $pairs/pair[@name eq "t_p"]/item
- return $field/pair[@name eq "k"] || "=" || $field/pair[@name eq "v"], "&"),
- "UTF-8")
- let $log := console:log($token-request)
- return $token-request
- )
- else ()
- };
- declare function interceptor:redirection-response($state)
- {
- let $completion-uri := "/exist/restxq/oauth-interceptor/complete"
- return
- <rest:response>
- <http:response status="302">
- <http:header
- name="location"
- value="{ $completion-uri || xs:string('?state=') || $state }">
- </http:header>
- </http:response>
- </rest:response>
- };
- declare function interceptor:registration-document($state)
- {
- collection($interceptor:DATA-PATH)/registration[@state eq $state]
- };
- declare function interceptor:registration-exists($state)
- {
- exists(collection($interceptor:DATA-PATH)/registration[@state eq $state])
- };
- declare function interceptor:clear-secrets($state)
- {
- if(interceptor:registration-exists($state)) then
- (
- (: TODO - if u_dbg=true then do not delete secrets :)
- update delete
- interceptor:registration-document($state)
- /request/json/pair[@name = ('s','i','k')]
- )
- else ()
- };
- declare
- %rest:GET
- %rest:path("/oauth-interceptor/complete")
- %rest:query-param("state", "{$state}")
- %rest:produces("text/html")
- %output:method("html")
- function interceptor:callback($state)
- {
- let $errors := interceptor:registration-document($state)/error
- let $back-color := if(empty($errors)) then '#00CCFF' else '#FF0000'
- return
- element div {
- attribute style {
- "background: " || $back-color ||"; height: 300px; top: 50%; tranfsorm: translateY(-50%); align-items:center;display:flex;justify-content:center;"
- },
- element div {
- element img {
- attribute src { "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Oauth_logo.svg/180px-Oauth_logo.svg.png" }
- }
- },
- element div {
- if(empty($errors)) then
- (
- element p { "Authentication complete." },
- element p { "You may now return to your application." },
- element p {
- attribute style { "color: #FF0000" },
- "Haptix Games provides this redirect service for testing purposes only.",
- element br { },
- "Server-side source code is included with the asset."
- }
- )
- else
- (
- element p {
- attribute style { "color: #FFFFFF" },
- "Authentication failed!"
- },
- for $error at $position in $errors
- order by $error/@timeStamp ascending
- return element div { element p { element small { "Exception " || $position || "." } }, element blockquote { element small { $error } } },
- element p {
- attribute style { "color: #FFFFFF" },
- "Return to your application and try again or contact the application's developer."
- }
- )
- }
- }
- };
- declare
- %rest:GET
- %rest:path("/oauth-interceptor/retrieve-response")
- %rest:query-param("state", "{$state}")
- %rest:consumes("multipart/form-data")
- %rest:produces("application/json")
- %output:media-type("application/json")
- %output:method("json")
- function interceptor:retrieve-response($state)
- {
- if(interceptor:registration-exists($state)) then
- (
- if(exists(interceptor:registration-document($state)/redeemed)) then
- (
- interceptor:clear-secrets($state),
- <rest:response>
- <http:response status="410">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>
- )
- else
- (
- if(exists(interceptor:registration-document($state)/error)) then
- (
- interceptor:clear-secrets($state),
- <rest:response>
- <http:response status="403">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>
- )
- else if(not(exists(interceptor:registration-document($state)/response))) then
- (
- interceptor:clear-secrets($state),
- <rest:response>
- <http:response status="404">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>
- )
- else
- (
- interceptor:clear-secrets($state),
- <rest:response>
- <http:response status="200">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>,
- element root {
- for $pair in interceptor:registration-document($state)/response/json/pair
- return element r {
- attribute json:array { true() },
- element k { $pair/@name/string() },
- element v { $pair/string() }
- }
- }
- ),
- update insert element
- redeemed {
- attribute timeStamp { util:system-dateTime() }
- } into interceptor:registration-document($state),
- update delete
- interceptor:registration-document($state)/response/json
- )
- )
- else
- (
- interceptor:clear-secrets($state),
- <rest:response>
- <http:response status="404">
- <http:header name="Access-Control-Allow-Origin" value="*"/>
- </http:response>
- </rest:response>
- )
- };
- (:let $ab := update insert element dbg { element at-resp { $at } } into $registration-document:)
- (:let $body :=
- <httpclient:fields>
- <httpclient:field name="code" value="{ $code }" type="string"/>
- <httpclient:field name="client_id" value="{ $registration-document/request/json/pair[@name eq "i"] }" type="string"/>
- <httpclient:field name="client_secret" value="{ $registration-document/request/json/pair[@name eq "k"] }" type="string"/>
- <httpclient:field name="redirect_uri" value="{ $registration-document/request/json/pair[@name eq "r_u"] }" type="string"/>
- {
- for $auth-field in $registration-document/request/json/pair[@name eq "t_p"]/item
- return <httpclient:field name="{ $auth-field/pair[@name eq "k"] }" value="{ $auth-field/pair[@name eq "v"] }" type="string"/>
- }
- </httpclient:fields>
- let $aa := update insert element dbg { element at-fields { $body } } into $registration-document
- let $at := httpclient:post-form(
- $registration-document/request/json/pair[@name eq "t_u"],
- $body,
- false(),
- <headers><header name="Content-Type" value="application/x-www-form-urlencoded"/></headers>)
- :)
Add Comment
Please, Sign In to add comment