diff --git a/extra/oauth2/authors.txt b/extra/oauth2/authors.txt new file mode 100644 index 0000000000..8c0a152f90 --- /dev/null +++ b/extra/oauth2/authors.txt @@ -0,0 +1 @@ +Björn Lindqvist diff --git a/extra/oauth2/oauth2-tests.factor b/extra/oauth2/oauth2-tests.factor new file mode 100644 index 0000000000..605e1ec3d4 --- /dev/null +++ b/extra/oauth2/oauth2-tests.factor @@ -0,0 +1,31 @@ +USING: kernel oauth2 tools.test urls ; +IN: oauth2.tests + +! oauth2>auth-uri +{ + URL" https://github.com/login/oauth/authorize?client_id=1234&scope=user&redirect_uri=test-pest&state=abcd&response_type=code&access_type=offline" +} [ + "https://github.com/login/oauth/authorize" + "https://github.com/login/oauth/access_token" + "test-pest" + "1234" "password" "user" + { { "state" "abcd" } } oauth2 boa oauth2>auth-uri +] unit-test + +! token-params +{ + { + { "client_id" "1234" } + { "client_secret" "password" } + { "redirect_uri" "test-pest" } + { "state" "abcd" } + { "code" "hej" } + { "grant_type" "authorization_code" } + } +} [ + "https://github.com/login/oauth/authorize" + "https://github.com/login/oauth/access_token" + "test-pest" + "1234" "password" "user" { { "state" "abcd" } } oauth2 boa + "hej" token-params +] unit-test diff --git a/extra/oauth2/oauth2.factor b/extra/oauth2/oauth2.factor new file mode 100644 index 0000000000..7b51155b40 --- /dev/null +++ b/extra/oauth2/oauth2.factor @@ -0,0 +1,68 @@ +! Copyright (C) 2016 Björn Lindqvist. +! See http://factorcode.org/license.txt for BSD license. +USING: accessors arrays assocs http.client io json.reader kernel +sequences unicode urls webbrowser ; +IN: oauth2 + +TUPLE: oauth2 + auth-uri + token-uri + redirect-uri + client-id + client-secret + scope + extra-params ; + +: set-query-params ( url params -- url ) + [ first2 swap set-query-param ] each ; + +: string+params>url ( string params -- url ) + [ >url ] dip set-query-params ; + +: console-prompt ( query -- str ) + write flush readln [ blank? ] trim ; + +: token-params ( oauth2 code -- params ) + [ + [ + [ client-id>> "client_id" swap 2array ] + [ client-secret>> "client_secret" swap 2array ] + [ redirect-uri>> "redirect_uri" swap 2array ] tri 3array + ] [ extra-params>> ] bi append + ] [ + "code" swap 2array + ] bi* suffix { + { "grant_type" "authorization_code" } + } append ; + +: auth-params ( oauth2 -- params ) + [ + [ client-id>> "client_id" swap 2array ] + [ scope>> "scope" swap 2array ] + [ redirect-uri>> "redirect_uri" swap 2array ] tri 3array + ] [ extra-params>> ] bi append { + { "response_type" "code" } + { "access_type" "offline" } + } append ; + +: oauth2>auth-uri ( oauth2 -- uri ) + [ auth-uri>> ] [ auth-params ] bi string+params>url ; + +: post-token-request ( params token-uri -- token ) + dup header>> "application/json" "Accept" rot set-at + http-request nip json> "access_token" of ; + +! Other flows can be useful to support too. +: console-flow ( oauth2 -- token ) + [ oauth2>auth-uri open-url ] [ + [ "Enter verification code: " console-prompt token-params ] + [ token-uri>> ] bi + post-token-request + ] bi ; + +! Using the token to access secured resources. +: add-token ( request url -- ) + "Bearer " prepend "Authorization" rot header>> set-at ; + +: oauth-http-get ( url token -- response data ) + [ dup ] dip add-token http-request ;