The last post about ODATA in the Google App Engine (python) allowed anyone to create, update or delete the models. Google App Engine (GAE) can authenticate Google users or users of you Google Apps domain. Using this feature, we can authenticate users of our C# application and use the authentication token when submitting calls to our OData service. The following class is based on the work found in here.
class GAEAuthentication
{
public string Authenticate(string gaeAppBaseUrl, string googleUserName, String googlePassword, string yourClientApp, string is_admin, bool is_dev_env)
{
string googleCookie;
String gaeAppLoginUrl = gaeAppBaseUrl + "_ah/login";
String yourGaeAuthUrl = gaeAppBaseUrl + "odata.svc/";
String googleLoginUrl;
if (is_dev_env)
{
googleLoginUrl = gaeAppLoginUrl;
}
else
{
googleLoginUrl = "https://www.google.com/accounts/ClientLogin";
}
// prepare the auth request
HttpWebRequest authRequest = (HttpWebRequest)HttpWebRequest.Create(googleLoginUrl);
if (is_dev_env)
{
// prepare the data we will post to the login page
String queryData = "email=" + HttpUtility.UrlEncode(googleUserName) + "&" +
"admin=" + HttpUtility.UrlEncode(is_admin) + "&" +
"action=login" + "&" +
"continue=" + HttpUtility.UrlEncode(yourGaeAuthUrl);
authRequest = (HttpWebRequest)HttpWebRequest.Create(googleLoginUrl + "?" + queryData);
authRequest.Method = "GET";
authRequest.AllowAutoRedirect = false;
// get the response
HttpWebResponse authResponse = (HttpWebResponse)authRequest.GetResponse();
googleCookie = authResponse.Headers["Set-Cookie"].Split(';')[0];
//authRequest.AllowAutoRedirect = false;
}
else
{
authRequest.Method = "POST";
authRequest.ContentType = "application/x-www-form-urlencoded";
authRequest.AllowAutoRedirect = false;
// prepare the data we will post to the login page
String postData = "Email=" + HttpUtility.UrlEncode(googleUserName) + "&" +
"Passwd=" + HttpUtility.UrlEncode(googlePassword) + "&" +
"service=" + HttpUtility.UrlEncode("ah") + "&" +
"admin=" + HttpUtility.UrlEncode(is_admin) + "&" +
"source=" + HttpUtility.UrlEncode(yourClientApp) + "&" +
"accountType=" + HttpUtility.UrlEncode("HOSTED_OR_GOOGLE");
byte[] buffer = Encoding.ASCII.GetBytes(postData);
authRequest.ContentLength = buffer.Length;
// submit the request
Stream postDataStr = authRequest.GetRequestStream();
postDataStr.Write(buffer, 0, buffer.Length);
postDataStr.Flush();
postDataStr.Close();
// get the response
HttpWebResponse authResponse = (HttpWebResponse)authRequest.GetResponse();
Stream responseStream = authResponse.GetResponseStream();
StreamReader responseReader = new StreamReader(responseStream);
// look through the response for an auth line
String authToken = null;
String nextLine = responseReader.ReadLine();
while (nextLine != null)
{
if (nextLine.StartsWith("Auth="))
{
// remove the 'Auth=' from the start
// of the string
// because when we give it back to
// google it needs to be 'auth='
// (lower-case 'a') and it is
// case-sensitive
authToken = nextLine.Substring(5);
}
nextLine = responseReader.ReadLine();
}
// cleanup
responseReader.Close();
authResponse.Close();
// prepare the redirect request
String cookieReqUrl = gaeAppLoginUrl + "?" +
"continue=" + HttpUtility.UrlEncode(yourGaeAuthUrl) + "&" +
"auth=" + HttpUtility.UrlEncode(authToken);
// prepare our HttpWebRequest
HttpWebRequest cookieRequest = (HttpWebRequest)WebRequest.Create(cookieReqUrl);
cookieRequest.Method = "GET";
cookieRequest.ContentType = "application/x-www-form-urlencoded";
cookieRequest.AllowAutoRedirect = false;
// retrieve HttpWebResponse with the google cookie
HttpWebResponse cookieResponse = (HttpWebResponse)cookieRequest.GetResponse();
googleCookie = cookieResponse.Headers["Set-Cookie"];
}
return googleCookie;
}
}
The class above authenticates the user and then returns token cookie. To allow the built-in authentication in GAE to work with our client program, we need to store the cookie containing that token and use it in each call to the OData service. We accomplish that by using the SendingRequest event to set the Cookie. The following code uses the GAEAuthentication to authenticate as an admin in a developer instance:class Program
{
private static string _cookie;
static void Main(string[] args)
{
GAEAuthentication authenticator = new GAEAuthentication();
_cookie = authenticator.Authenticate("http://localhost:8080/", "test@example.com", "", "odata", "True", true);
GAEODATAService.model.default_container proxy = new GAEODATAService.model.default_container(new Uri("http://localhost:8080/odata.svc"));
proxy.SendingRequest += new EventHandler(proxy_SendingRequest);
proxy.AddToCashTransaction(new CashTransaction() { key = "0", amount_cents = 12098, date = DateTime.Now, description = "Online Bill" }); //must be one of set(['bird', 'dog', 'cat'])
proxy.SaveChanges();
var all_pets = proxy.Pet;
foreach (var pet in all_pets)
{
Console.Out.WriteLine("Pet {0}, weight: {1}, type: {2}", pet.name, pet.weight_in_pounds, pet.type);
}
Console.ReadLine();
}
///
/// Intercept proxy's SendingRequest so that we can add the Google authentication cookie to the request.
///
///
///
private static void proxy_SendingRequest(object sender, SendingRequestEventArgs evt)
{
evt.RequestHeaders["Cookie"] = _cookie;
}
}
You will need to configure your GAE app as well to require authentication (See this). One simple example is to allow only admins to add, update or delete by changing your app.yaml to read:- url: /odata.svc(/.*) script: odata-gae.py login: admin
Comments