OAuth2 should work. totally untested

Signed-off-by: Luca Fulchir <luca@fulchir.it>
parent 8c4dcf71
......@@ -2,3 +2,4 @@
.class
.jar
build/
.*.swo
......@@ -10,7 +10,7 @@ java:
- mkdir -p ${BUILDDIR} 2>/dev/null
${JAVAC} -cp ${JOLIEDIR}/jolie.jar -d ${BUILDDIR} jolie/net/utils/URLEncoder.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/commons-codec-1.6.jar -d ${BUILDDIR} jolie/net/utils/Hmac.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/httpcore-4.2.2.jar:${JARDIR}/httpclient-4.2.3.jar:${JARDIR}/commons-io-2.4.jar:${JARDIR}/commons-codec-1.6.jar -d ${BUILDDIR} jolie/net/utils/HTTP.java
${JAVAC} -cp ${JOLIEDIR}/jolie.jar:${JARDIR}/httpcore-4.2.2.jar:${JARDIR}/httpclient-4.2.3.jar:${JARDIR}/commons-io-2.4.jar:${JARDIR}/commons-codec-1.6.jar:${JOLIEDIR}/extensions/http.jar -d ${BUILDDIR} jolie/net/utils/HTTP.java
cd ${BUILDDIR} && jar cvf ${PACKAGE_NAME} ./jolie/net/utils/*class && cd ${CWD}
......
......@@ -8,6 +8,10 @@ send:
-redirect_uri (optional)
-scope (optional)
-state (optional)
google:
-approval_prompt (force) maybe optional
-access_type (offline) ?
auth: https://accounts.google.com/o/oauth2/auth
get:
-code (required)
-scope (optional if)
......@@ -22,6 +26,8 @@ send:
-code (required)
-redirect_uri (required)
-client_id (required)
google:
token: https://accounts.google.com/o/oauth2/token
get:
-access_token (required)
-token_type (required)
......@@ -33,6 +39,8 @@ error:
-bla-bla
-------------------------------
?
-------------------------------
AuthRequest:
-response_type (required = token)
-client_it (required)
......
......@@ -23,9 +23,9 @@ include "OAuth2Data.iol"
interface OAuth2Interface {
RequestResponse:
RequestToken (OAuth2Info) (OAuth2Info) throws wrong_answer,
getReferral (OAuth2Info) (string),
getAccess (OAuth2Info) (OAuth2_parsed)
Auth (OAuth2Info) (string),
Access (OAuth2Info) (OAuth2Info),
Refresh (OAuth2Info) (OAuth2Info),
}
outputPort OAuth2 {
......
......@@ -32,7 +32,7 @@ inputPort OAuth2 {
execution { concurrent}
main {
[AuthRequest (info) (newInfo) {
[Auth (info) (newInfo) {
newInfo << info;
http_data.URI = info.location_authorize;
toUpperCase@StringUtils (info.method) (http_data.method);
......@@ -53,8 +53,9 @@ scope (error_handler) {
throw(OAuth2_error)
);
//make HTTP request
DataAuthRequest@OAuth2Data (info) (data_query);
DataAuth@OAuth2Data (info) (data_query);
http_data.query << newInfo.query;
//we have to add the OAuth request to the previous data
for (count=#newInfo.query, count < (#newInfo.query + #data_query),
count++) {
http_data.query[count] << data_query[count - #newInfo.query]
......@@ -84,7 +85,7 @@ scope (error_handler) {
}
}]{nullProcess}
[AccessRequest (info) (newInfo) {
[Access (info) (newInfo) {
newInfo << info;
http_data.URI = info.location_access;
toUpperCase@StringUtils (info.method) (http_data.method);
......@@ -103,85 +104,93 @@ scope (error_handler) {
println@Console("could not send HTTP request")();
throw(OAuth2_error)
);
AccessRequest@OAuth2Data (info) (data_query);
DataAccess@OAuth2Data (info) (data_query);
for (count=#newInfo.query, count < (#newInfo.query + #data_query),
count++) {
http_data.query[count] << data_query[count - #newInfo.query]
};
sendRequest@HTTP(http_data) (answer);
// search for errors
search.pair = answer;
search.search = "access_token";
getPair@OAuth2Data (search)(newInfo.access_token);
newInfo.code = res;
if (info.hasChildren("state")) {
search.search = "state";
getPair@OAuth2Data (search)(res);
//check for equality... really fast this way, uh?
length@StringUtils(res)(ln1);
length@StringUtils(info.state)(ln2);
if (ln1 != ln2) {
throw(state_mismatch)
};
res.startswith=info.state;
startsWith(res)(strTest);
if (!strTest) {
throw(state_mismatch)
}
}
}]{nullProcess}
JsonToValue@HTTP(answer)(answer_json);
if (is_defined(answer_json.access_token) {
newInfo.access_token = answer_json.access_token;
undef(answer_json.access_token)
} else {
//access_token is required by rfc
throw (oauth2_answer)
};
if (is_defined(answer_json.refresh_token) {
newInfo.refresh_token = answer_json.refresh_token;
undef(answer_json.refresh_token)
};
if (is_defined(answer_json.expires_in) {
newInfo.expires_in = answer_json.expires_in
undef(answer_json.expires_in)
};
if (is_defined(answer_json.token_type) {
newInfo.token_type = answer_json.token_type
undef(answer_json.token_type)
} else {
//token_type is required by rfc
throw (oauth2_answer)
};
newInfo.json = answer_json;
[RequestToken (info) (token) {
}]{nullProcess}
http_data.URI = info.location_request;
[Refresh (info) (newInfo)
{
newInfo << info;
http_data.URI = info.location_access;
toUpperCase@StringUtils (info.method) (http_data.method);
info.method = http_data.method;
newInfo.method = info.method;
http_data.headers[0].name = "userAgent";
http_data.headers[0].value = "Jolie-OAuth";
http_data.headers[1].name = "Authorization";
DataRequestToken@OAuth2Data (info) (http_data.headers[1].value);
http_data.headers[0].value = "Jolie-OAuth2";
scope (error_handler) {
install(not_found =>
println@Console("error on request") ();
throw(OAuth2_error)
state_mismatch =>
println@Console("wrong state in reply") ();
throw(OAuth2_error)
send_error =>
println@Console("could not send HTTP request")();
throw(OAuth2_error)
);
DataRefresh@OAuth2Data (info) (data_query);
for (count=#newInfo.query, count < (#newInfo.query + #data_query),
count++) {
http_data.query[count] << data_query[count - #newInfo.query]
};
sendRequest@HTTP(http_data) (answer);
token << info;
parseAnswer@OAuth2Data(answer) (tmp);
tmp.search = "oauth_token";
getPair@OAuth2Data(tmp)(token.token);
tmp.search = "oauth_token_secret";
getPair@OAuth2Data(tmp)(token.token_secret)
}]{nullProcess}
[getReferral (info) (output) {
http_data.URI = info.location_authorize;
http_data.headers[0].name = "userAgent";
http_data.headers[0].value = "Jolie-OAuth";
http_data.query[0].name = "oauth_token";
http_data.query[0].value = info.token;
if (is_defined(info.callback)) {
http_data.query[1].name = "oauth_callback";
http_data.query[1].value = info.callback
JsonToValue@HTTP(answer)(answer_json);
if (is_defined(answer_json.access_token) {
newInfo.access_token = answer_json.access_token;
undef(answer_json.access_token)
} else {
//access_token is required by rfc
throw (oauth2_answer)
};
http_data.method = "GET";
buildURI@HTTP (http_data) (output)
}] {nullProcess}
[getAccess (info) (output) {
http_data.URI = info.location_access;
http_data.headers[0].name = "userAgent";
http_data.headers[0].value = "Jolie-OAuth";
http_data.method = "POST";
http_data.headers[1].name = "Authorization";
DataAuthenticate@OAuth2Data (info) (http_data.headers[1].value);
sendRequest@HTTP(http_data) (answer);
if (is_defined(answer_json.refresh_token) {
newInfo.refresh_token = answer_json.refresh_token;
undef(answer_json.refresh_token)
};
if (is_defined(answer_json.expires_in) {
newInfo.expires_in = answer_json.expires_in
undef(answer_json.expires_in)
};
if (is_defined(answer_json.token_type) {
newInfo.token_type = answer_json.token_type
undef(answer_json.token_type)
} else {
//token_type is required by rfc
throw (oauth2_answer)
};
newInfo.json = answer_json;
parseAnswer@OAuth2Data (answer) (output)
}]{nullProcess}
}] {nullProcess}
}
......@@ -28,6 +28,7 @@ type OAuth2Info:void {
.location_access :string
.method :string
.client_id :string
.client_secret :string
.code? :string
.redirect_uri? :string
.scope? :string
......@@ -37,6 +38,7 @@ type OAuth2Info:void {
.refresh_token? :string
.expires? :int
// HTTP additional user-specified parameters
.json? :any
.headers? :OAuth2Pair
.query? :OAuth2Pair
}
......@@ -53,10 +55,8 @@ type OAuth2_pairSearch:void {
interface OAuth2DataInterface {
RequestResponse:
DataAuthRequest (OAuth2Info) (HTTPpair),
DataRequestToken (OAuth2Info) (string),
DataAuthenticate (OAuth2Info) (string),
DataAuth (OAuth2Info) (HTTPpair),
DataAccess (OAuth2Info) (HTTPpair),
parseAnswer (string) (OAuth2_parsed) throws wrong_answer,
getPair (OAuth2_pairSearch) (string) throws not_found
}
......
......@@ -35,7 +35,7 @@ inputPort OAuth2Data{
execution {concurrent}
main {
[DataAuthRequest (info) (data)
[DataAuth (info) (data)
{
data[0].name = "client_id";
data[0].value = info.client_id;
......@@ -44,19 +44,19 @@ main {
data[1].value = "code";
data[1].format= "application/x-www-form-urlencoded";
count = #data;
if (info.hasChildren("redirect_uri")) {
if (is_defined(info.redirect_uri) {
data[count].name = "redirect_uri";
data[count].value = info.redirect_uri;
data[count].format= "application/x-www-form-urlencoded";
count++
};
if (info.hasChildren("scope")) {
if (is_defined(info.scope) {
data[count].name = "scope";
data[count].value = info.scope;
data[count].format= "application/x-www-form-urlencoded";
count++
};
if (info.hasChildren("state")) {
if (is_defined(info.state) {
data[count].name = "state";
data[count].value = info.state;
data[count].format= "application/x-www-form-urlencoded";
......@@ -64,116 +64,34 @@ main {
}
}]{nullProcess)
[DataRequestToken (info) (postString)
{
scope (error_handler) {
install( Hmac_fault =>
println@Console("HmacError") ();
throw(Hmac_fault)
);
// getCurrentTimeMillis is a temporary interface, but there's
// nothing better :(
getCurrentTimeMillis@Time(void)(tmp_timestamp);
tmp_timestamp = tmp_timestamp / 1000;
// a little too much as a nonce, but it's easier this way :p
createSecureToken@SecurityUtils(void)(oauth_nonce);
toSort.item[0] = "oauth_signature_method=HMAC-SHA1";
toSort.item[1] = "oauth_timestamp=" + tmp_timestamp;
toSort.item[2] = "oauth_nonce=" + oauth_nonce;
toSort.item[3] = "oauth_version=1.0";
toSort.item[4] = "oauth_consumer_key=" + info.consumer_key;
count = 4;
if (is_defined(info.callback)) {
count = count + 1;
toSort.item[count] = "oauth_callback=" + info.callback
};
sort@StringUtils (toSort) (sorted);
toEncode.encoding = "UTF8";
toEncode = info.location_request;
URLencode@HTTP (toEncode) (encodedLocation);
toHash.secret = "";
if (is_defined(info.secret))
toHash.secret = info.secret;
toHash.secret = toHash.secret + "&";
toHash.data = info.method + "&" + encodedLocation + "&";
tmp = "";
for (i = 0, i < count, i++) {
tmp = tmp + sorted.item[i] + "&"
};
tmp = tmp + sorted.item[count];
toEncode = tmp;
URLencode@HTTP (toEncode) (encodedParams);
toHash.data = toHash.data + encodedParams;
sha1@Hmac (toHash) (tmpSignature);
toEncode = tmpSignature;
URLencode@HTTP (toEncode) (signature);
postString = "OAuth " +
"oauth_consumer_key=\"" + info.consumer_key + "\"," +
"oauth_signature_method=\"HMAC-SHA1\"," +
"oauth_timestamp=\"" + tmp_timestamp + "\"," +
"oauth_nonce=\"" + oauth_nonce + "\"," +
"oauth_version=\"1.0\"," +
"oauth_signature=\"" + signature + "\"";
if ( is_defined(info.callback))
postString = postString +
",oauth_callback=\"" + info.callback + "\""
}
}]{nullProcess}
[DataAuthenticate (info) (postString)
[DataAccess (info) (data)
{
// getCurrentTimeMillis is a temporary interface, but there's
// nothing better :(
getCurrentTimeMillis@Time(void)(tmp_timestamp);
tmp_timestamp = tmp_timestamp / 1000;
// a little too much as a nonce, but it's easier this way :p
createSecureToken@SecurityUtils(void)(oauth_nonce);
toSort.item[0] = "oauth_signature_method=HMAC-SHA1";
toSort.item[1] = "oauth_timestamp=" + tmp_timestamp;
toSort.item[2] = "oauth_nonce=" + oauth_nonce;
toSort.item[3] = "oauth_version=1.0";
toSort.item[4] = "oauth_consumer_key=" + info.consumer_key;
toSort.item[5] = "oauth_token=" + info.token;
sort@StringUtils (toSort) (sorted);
toEncode.encoding = "UTF8";
toEncode = info.location_access;
URLencode@HTTP (toEncode) (encodedLocation);
tmp = "";
for (i = 0, i < (#toSort.item - 1), i++) {
tmp = tmp + sorted.item[i] + "&"
data[0].name = "client_id";
data[0].value = info.client_id;
data[0].format= "application/x-www-form-urlencoded";
data[1].name = "grant_type";
data[1].value = "authorization_code";
data[1].format= "application/x-www-form-urlencoded";
data[2].name = "code";
data[2].value = info.code;
data[2].format= "application/x-www-form-urlencoded";
data[3].name = "client_secret";
data[3].value = info.client_secret;
data[3].format= "application/x-www-form-urlencoded";
count = #data;
if (is_defined(info.redirect_uri) {
data[count].name = "redirect_uri";
data[count].value = info.redirect_uri;
data[count].format= "application/x-www-form-urlencoded";
count++
};
tmp = tmp + sorted.item[#sorted.item - 1];
toEncode = tmp;
URLencode@HTTP (toEncode) (encodedParams);
toHash.secret = info.secret + "&" + info.token_secret;
toHash.data = info.method + "&" + encodedLocation + "&" + encodedParams;
sha1@Hmac (toHash) (tmpSignature);
toEncode = tmpSignature;
URLencode@HTTP (toEncode) (signature);
postString = "OAuth " +
"oauth_consumer_key=\"" + info.consumer_key + "\"," +
"oauth_signature_method=\"HMAC-SHA1\"," +
"oauth_timestamp=\"" + tmp_timestamp + "\"," +
"oauth_nonce=\"" + oauth_nonce + "\"," +
"oauth_version=\"1.0\"," +
"oauth_token=\"" + info.token + "\"," +
"oauth_signature=\"" + signature + "\""
}]{nullProcess}
if (is_defined(info.scope) {
data[count].name = "scope";
data[count].value = info.scope;
data[count].format= "application/x-www-form-urlencoded";
count++
}
}]{nullProcess)
[parseAnswer (answer) (token) {
scope (parsing) {
......
include "console.iol"
include "OAuth1.iol"
include "security_utils.iol"
include "time.iol"
include "string_utils.iol"
execution { concurrent }
type codeRequest:void {
.code? :string
.error? :string
}
interface OAuth2_HTTP {
RequestResponse:
code(codeRequest)(string)
}
inputPort HTTP_Get {
Location: "socket://localhost:8055/"
Protocol: http
Interfaces: OAuth2_HTTP
}
main
{
// data needed for the connection: where to connect, how and who we are
data.location_authorize = "https://accounts.google.com/o/oauth2/auth";
data.location_access = "https://accounts.google.com/o/oauth2/token";
data.method = "GET";
data.client_id = "393076792151.apps.googleusercontent.com";
data.client_secret = "26H4P4_MJ8lBqXxNb-4dAqTL";
data.redirect_uri = "http://localhosti:8055";
data.scope = "https://www.googleapis.com/auth/plus.me";
// send the message
Auth@OAuth2 (data) (referr);
println@Console ("auth this app with " + referr)();
//now we wait for the http redirect which will bring us
// the authentication code needed.
ERR="Error"
[code(codeRequest) (out)
{
out = "OK"
if (is_defined(codeRequest.error)) {
println@Console("Error while authenticating app")();
out = ERR
} else {
if (!is_defined(codeRequest.code)) {
println@Console("We got a redirect, no code included")();
out = ERR
};
if (is_defined(data.state)) {
if (!is_defined(codeRequest.state)) {
println@Console("We got a redirect, but it wasn't ours")();
out = ERR
};
length@StringUtils(codeRequest.state)(ln1);
length@StringUtils(data.state)(ln1);
if (ln1 != ln2) {
println@Console("We got a redirect, but it wasn't ours")();
out = ERR
};
codeRequest.state.startsWith = data.state;
startsWith(codeRequest.state)(test);
if (!test) {
println@Console("We got a redirect, but it wasn't ours")();
out = ERR
}
} else {
if (is_defined(codeRequest.state)) {
println@Console("State? What state??")();
out = ERR
}
}
}
}]{
if (out == ERR) {
throw (not_our_redirect)
};
data.code = codeRequest.code;
data.method = "POST";
// next step in Auth2 authentication.
Access@OAuth2 (data) (answer)
}
}
......@@ -47,7 +47,9 @@ RequestResponse:
parse_www_form (string) (HTTPpair) throws parse_error,
sendRequest (HTTPRequest) (string) throws send_error,
buildURI (HTTPRequest) (string) throws buildURI_fault,
URLencode (HTTPURLEncoding) (string) throws Encoding_fault
URLencode (HTTPURLEncoding) (string) throws Encoding_fault,
JsonToValue (String) (Value) throws cannot_translate
}
outputPort HTTP {
......
......@@ -33,8 +33,10 @@ import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.commons.io.IOUtils;
import org.apache.commons.codec.net.URLCodec;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import jolie.net.http.json.JsonUtils;
import jolie.runtime.JavaService;
import jolie.runtime.Value;
import jolie.runtime.ValueVector;
......@@ -101,7 +103,19 @@ public static String buildURI (Value req) throws FaultException
}
@RequestResponse
public static ValueVector parse (Value data) throws FaultException
public static Value JsonToValue (String json) throws FaultException
{
try {
Value ret = Value.create();
JsonUtils.parseJsonIntoValue(new StringReader(json), ret);
return ret;
} catch (Exception e) {
throw new FaultException("cannot_translate", e);
}
}
@RequestResponse
public static ValueVector parse_www_form (Value data) throws FaultException
{
// URLEncoded values to parsed pairs
URLEncodedUtils decode = new URLEncodedUtils();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment