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 EventHandlerYou 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:(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; } }
- url: /odata.svc(/.*) script: odata-gae.py login: admin
Comments