Google App Engine с интерфейсом ClientLogin для Objective-C - PullRequest
10 голосов
/ 23 января 2009

У меня такая же проблема в этой предыдущей публикации на stackoverflow.com .

В частности, мне кажется, что я могу правильно получить токен "Auth", но попытка использовать его в заголовке при доступе к более поздним страницам все равно просто возвращает мне HTML-код страницы входа.

По ссылкам, связанным с этим постом, я определил, что вам нужно сделать следующий звонок на этот URL .

После обращения к URL-адресу вы получите файл cookie ACSID, который затем необходимо будет передать в последующих вызовах, чтобы сохранить аутентифицированное состояние.

Когда я запрашивал этот файл cookie, я читал различные посты, в которых говорилось, что вам нужно указать свой исходный токен авторизации, добавив его в строку запроса так, чтобы:

?auth=this_is_my_token

Я также читал, что вы должны установить его в заголовке http, как описано в документации Google , чтобы имя / значение заголовка http было:

Authorization: GoogleLogin auth=yourAuthToken

Я испробовал оба подхода и не вижу возвращенных файлов cookie. Я использовал Wireshark, LiveHttpHeaders для Firefox и простые операторы NSLog, пытаясь увидеть, возвращено ли что-нибудь подобное.

Ниже приведен фрагмент кода, который я использовал.

NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://yourapp.appspot.com/_ah/login?auth=%@", [token objectForKey:@"Auth"]]];
NSHTTPURLResponse* response;
NSError* error;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setValue:[NSString stringWithFormat:@"GoogleLogin auth=%@", [token objectForKey:@"Auth"]] forHTTPHeaderField:@"Authorization"];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];  

//show me all header fields
NSLog([[response allHeaderFields] description]);

//show me the response
NSLog(@"%@", [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]);
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:@"http://yourapp.appspot.com/_ah/login"]];

//show me all cookies
for (NSHTTPCookie *cookie in all) 
{
    NSLog(@"Name: %@ : Value: %@", cookie.name, cookie.value); 
}

Надеюсь, вы можете использовать ClientLogin для кода Google App Engine.

Ответы [ 7 ]

16 голосов
/ 26 января 2009

Добавление примера кода к этому вопросу, потому что кто-то связался со мной напрямую по поводу моего решения. Обратите внимание, что вы должны установить параметр «service» равным «ah» в начальном запросе токена.

Первоначальный запрос токена [выполнено синхронно] ПРИМЕЧАНИЕ. Для параметра «service» установлено значение «ah», а для «source» просто установлено значение «myapp», вам следует использовать имя вашего приложения.

//create request
NSString* content = [NSString stringWithFormat:@"accountType=HOSTED_OR_GOOGLE&Email=%@&Passwd=%@&service=ah&source=myapp", [loginView username].text, [loginView password].text];
NSURL* authUrl = [NSURL URLWithString:@"https://www.google.com/accounts/ClientLogin"];
NSMutableURLRequest* authRequest = [[NSMutableURLRequest alloc] initWithURL:authUrl];
[authRequest setHTTPMethod:@"POST"];
[authRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];
[authRequest setHTTPBody:[content dataUsingEncoding:NSASCIIStringEncoding]];

NSHTTPURLResponse* authResponse;
NSError* authError;
NSData * authData = [NSURLConnection sendSynchronousRequest:authRequest returningResponse:&authResponse error:&authError];  

NSString *authResponseBody = [[NSString alloc] initWithData:authData encoding:NSASCIIStringEncoding];

//loop through response body which is key=value pairs, seperated by \n. The code below is not optimal and certainly error prone. 
NSArray *lines = [authResponseBody componentsSeparatedByString:@"\n"];
NSMutableDictionary* token = [NSMutableDictionary dictionary];
for (NSString* s in lines) {
    NSArray* kvpair = [s componentsSeparatedByString:@"="];
    if ([kvpair count]>1)
        [token setObject:[kvpair objectAtIndex:1] forKey:[kvpair objectAtIndex:0]];
}

//if google returned an error in the body [google returns Error=Bad Authentication in the body. which is weird, not sure if they use status codes]
if ([token objectForKey:@"Error"]) {
    //handle error
};

Следующим шагом будет запуск вашего приложения на движке приложений Google, который предоставит вам файл cookie ASCID. Я не уверен, почему существует этот дополнительный шаг, похоже, это проблема на стороне Google и, возможно, почему GAE в настоящее время нет в их списке obj-c Google Data API API. Мои тесты показывают, что у меня есть , чтобы запросить cookie для синхронизации с GAE. Кроме того, обратите внимание, что я ничего не делаю с cookie. Кажется, что, просто запросив его и получив готовый файл, будущие запросы будут автоматически содержать cookie. Я не уверен, что это приложение для iPhone, потому что мое приложение - приложение для iPhone, но я не до конца понимаю, что происходит с этим файлом cookie. ПРИМЕЧАНИЕ: использование «myapp.appspot.com».

NSURL* cookieUrl = [NSURL URLWithString:[NSString stringWithFormat:@"http://myapp.appspot.com/_ah/login?continue=http://myapp.appspot.com/&auth=%@", [token objectForKey:@"Auth"]]];
    NSLog([cookieUrl description]);
    NSHTTPURLResponse* cookieResponse;
    NSError* cookieError;
    NSMutableURLRequest *cookieRequest = [[NSMutableURLRequest alloc] initWithURL:cookieUrl];

    [cookieRequest setHTTPMethod:@"GET"];

    NSData* cookieData = [NSURLConnection sendSynchronousRequest:cookieRequest returningResponse:&cookieResponse error:&cookieError];   

Наконец, я могу опубликовать JSON в моем приложении Gae. ПРИМЕЧАНИЕ: приведенный ниже фрагмент является асинхронным запросом. Мы можем обработать ответы, реализовав didReceiveResponse, didReceiveData, didFailWIthError.

NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myapp.appspot.com/addRun?auth=%@", mytoken]];
    NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:@"my http body";

    NSURLConnection *connectionResponse = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    if (!connectionResponse) {
        NSLog(@"Failed to submit request");
    } else {
        NSLog(@"Request submitted");
    }
1 голос
/ 09 марта 2010

Я создал несколько классов obj-c для реализации ClientLogin, включая поддержку Google App Engine:

http://github.com/cameronr/GoogleAppEngineAuth

1 голос
/ 28 февраля 2009

1-й - спасибо за отличный пост, он действительно начал меня.

2-й - я подключил его к своему приложению, пытаясь отправить POST в GAE при аутентификации

Это запрос строится при POSTing, как только вы приобрели авторизированный токен:

    NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];


[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:@"POST"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"image/png" forHTTPHeaderField:@"Content-Type"];
[request setHTTPBody:postData];

[request setValue:authtoken forHTTPHeaderField:@"auth"];  // <-- the magic
  • Mattb
1 голос
/ 23 января 2009

Проверьте код, который делает это в официальном SDK. В последнем выпуске SDK он даже разделен на собственный файл .

0 голосов
/ 06 марта 2010

Использование HOSTED_OR_GOOGLE является неправильным, и я объясню почему.

В мире Google существует два вида учетных записей. Те, что вы создаете для GMail и т. Д., Являются аккаунтами Google. Те, которые вы создаете для приложений для доменов, являются «размещенными» учетными записями. Вы можете использовать электронную почту размещенного аккаунта для создания аккаунта Google, создавая таким образом адрес электронной почты, который связан с обоими типами аккаунтов.

Ваше приложение Google App Engine можно настроить для работы с (1) учетными записями Google или (2) размещенными учетными записями для определенного домена.

Предположим, что мы разрабатываем приложение для учетных записей Google. Пользователь вводит адрес электронной почты, связанный с учетной записью Google и размещенной учетной записью. Google будет использовать свою учетную запись Google для входа в систему. Это все отлично работает.

Теперь, если мы используем ClientLogin с тем же адресом электронной почты и используем HOSTED_OR_GOOGLE для типа учетной записи, вход будет успешным, но он будет использовать размещенную учетную запись, так как размещенная учетная запись имеет приоритет. Как я упоминал выше, вы не можете использовать размещенную учетную запись для приложения, которое ожидает учетную запись Google. Таким образом, аутентификация не будет работать.

Таким образом, при использовании ClientLogin для аутентификации в приложении Google App Engine вам необходимо использовать GOOGLE для типа учетной записи, если приложение предназначено для учетных записей Google, или HOSTED для типа учетной записи, если приложение предназначено для домена.

0 голосов
/ 20 февраля 2010

Спасибо за этот пост и особенно за ответ от Кита, но он не работает для меня. Даже если мне кажется, что это нормально ... очень странно.

Я проверяю этот пост ( Как получить доступ к аутентифицированному сервису Google App Engine из (не веб-клиента) Python? Я проверяю это, и оно работает.

И целевой код C, предложенный Китом, действительно похож на код Python.

Но когда я пытаюсь получить токен «Auth», authData содержит Error = BadAuthentication.

Кто-нибудь получил представление о возможных проблемах?

0 голосов
/ 03 декабря 2009

Обратите внимание, что Google недавно изменил способ обозначения ошибки авторизации. Они использовали для добавления токена ошибки в ответ. Теперь они просто возвращают статус 403 (Запрещено). Это сломало мой код!

...