Jeden endpoint API zwraca 401 Unauthorized podczas gdy inne działają

0

Cześć,

krótkim słowem wstępu -> próbuję uzyskać dostęp do endpointów API serwisu GTmerix (serwis, który testuje strony pod kątem powiedzmy wydajności), przykładowe endpointy serwisu:

https://gtmetrix.com/api/2.0/tests/{id_testu}
https://gtmetrix.com/api/2.0/reports/{id_raportu}/resources/lighthouse.json

Autentykacja do API wygląda w sposób następujący:

The GTmetrix API uses HTTP Basic Access Authentication as its authentication mechanism. Use your API key as the username and leave the password blank.

W ramach ich API korzystałem już z kilku/kilkunastu endpointów (zarówno POST jak i GET) i nigdy nie miałem z nimi problemów. Jedyny problem to z endpointem https://gtmetrix.com/api/2.0/tests/{id_testu} .

Kiedy wywołuje go z poziomu Postman'a wszystko działa ok, jednak kiedy wykonuje go z poziomu kodu (RestSharp) za każdym razem otrzymuje response 401 Unauthorized. To samo ma miejsce gdy nawet wygeneruje z Postman kod zapytania jako RestSharp i uruchomię w VS.

Case I
Wywołanie endpointa https://gtmetrix.com/api/2.0/reports/{id_raportu}/resources/lighthouse.json

Postman

RestSharp

var client = new RestClient("https://gtmetrix.com/api/2.0/reports/{id_raportu}/resources/lighthouse.json");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Basic {api_key}");
IRestResponse response = client.Execute(request);

Console.WriteLine($"Response status: {response.StatusCode}");
Console.WriteLine($"Response number: {(int)response.StatusCode}");

Debuger


Case II
Wywołanie problematycznego endpointa https://gtmetrix.com/api/2.0/tests/{id_testu}

Postman

RestSharp

var client = new RestClient("https://gtmetrix.com/api/2.0/tests/TfIhsql3");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Basic {api_key}");
IRestResponse response = client.Execute(request);

Console.WriteLine($"Response status: {response.StatusCode}");
Console.WriteLine($"Response number: {(int)response.StatusCode}");

Debuger

Jako ciekawostkę wklejam odpowiedź jaką uzyskałem z suportu GTmerix:

Czyli w logach nie widzą, że przesyłam klucz API, podczas gdy w przypadku problematycznego requesta otrzymuje response, że klucz został wysłany

2

Przy basic auth format jest następujący username:password i cała fraza ma być zakodowana base64 także nie wiem czy to nie działa przez przypadek:

request.AddHeader("Authorization", "Basic {api_key}");

Zauważ że dokumentacja podaje że trzeba to zrobić inaczej: https://restsharp.dev/usage/authenticators.html#using-simpleauthenticator

Moja hipoteza : się nie zakodował bo powinno być {api_key}: i to zakodowane base64. Być może inne endpointy w ogóle nie mają sprawdzania tego headera - co łatwo sprawdzić wystarczy go usunąć.

0

@0xmarcin: Dzięki za odpowiedź. Wysyłając requesta za pomocą RestSharp wysyłam zakodowany {api_key} w base64. Pierwsze co zrobiłem to dodanie : do {api_key}:

request.AddHeader("Authorization", "Basic {api_key}:");

W przypadku endpoint https://gtmetrix.com/api/2.0/reports/{id_raportu}/resources/lighthouse.json otrzymałem response 200 OK, w przypadku https://gtmetrix.com/api/2.0/tests/{id_testu} nadal wyrzuca 401 Unauthorized.

Zerknąłem na dokmunetację do której podesłałeś linka i zamiast request.AddHeader("Authorization", "Basic {api_key}:"); użyłem:

 client.Authenticator = new HttpBasicAuthenticator("{api_key_nie_zakodowany_do_base64}", "");

Efekt taki sam jak wyżej. W przypadku endpoint https://gtmetrix.com/api/2.0/reports/{id_raportu}/resources/lighthouse.json otrzymałem response 200 OK, w przypadku https://gtmetrix.com/api/2.0/tests/{id_testu} nadal wyrzuca 401 Unauthorized.

Sprawdziłem dodatkowo usunięcie hedera w przypadku endpoint https://gtmetrix.com/api/2.0/reports/{id_raportu}/resources/lighthouse.json-> otrzymałem response 401 Unauthorized.

Jako, że sam klucz API używam na razie w celach testowych i jest to jakiś tam free plan do testów podsyłam link do repo Github: https://github.com/awrobel196/GTMetrix-APITest-Repo/blob/master/ConsoleApp7/Program.cs

2

Hmm, dziwne być może plan free nie obejmuje dostępu do tego ostatniego end-pointu. Ja bym napisał do nich email z opisem sytuacji.

0

@0xmarcin: tak jak wspominałem wyżej, w Postman endpoint działa (nie działa tylko w RestSharp) . Co więcej, dostałem od nich odpowiedź, że sprawdzali w logach i jeśli wysyłam requesta przy pomocy Postman dochodzi do nich klucz API natomiast jeśli wysyłam z RestSharp z ich logów wynika, że w ogóle nie otrzymali klucza API.

1

Chciałem sprawdzić, ale dla kodu z GitHuba, niedziałający endpoint zwraca 404, a nie 401 :(

Anyway, z RestSharpa dla obu requestów dołączany jest taki sam nagłówek Authorization co sprawdziłem Fiddlerem, więc to nie jest tak, że sam RestSharp tutaj coś psuje.

0

@some_ONE: dzięki za pochylenie się nad tematem. Akurat te endpointy przechowują raporty, które wygasają po bodaj 48 godzinach stąd response 404. Zaktualizowałem repozytorium o nowy zaktualizowany endpoint. Jeśli znajdziesz chwilę czasu na sprawdzenie będę turbo wdzięczny.

1

Racja nawet z pustym hasłem RestSharp koduje prawidłowo: https://github.dev/restsharp/RestSharp/blob/e3280ab99f09aa080f5c49ea97bcaf28201a2050/src/RestSharp/Authenticators/HttpBasicAuthenticator.cs

Może warto przyjrzeć się innym header'om które się wysyłają, zrobić diff PostMan vs RestSharp, jakaś różnica musi być. Ja jestem na mac'u a nie mam tutaj setup'u DotNet'a.

3

No i wszystko jasne ;)

Request pod /api/2.0/tests/rlGPuuAA dostaje w zwrotce redirect 303 z Location: https://gtmetrix.com/api/2.0/reports/yIFECwVZ.
screenshot-20211119143041.png
Domyślnie RestSharp ma włączoną opcję FollowRedirects, ale w drugim requeście nie ustawia nagłówka Authorization, więc dostaje 401.

https://github.com/restsharp/RestSharp/issues/414

Spróbuj ustawić ten nagłówek bezpośrednio na kliencie, a nie requeście - client.AddDefaultHeader().

0

@some_ONE: Zrobiłem tak:

var client = new RestClient("https://gtmetrix.com/api/2.0/tests/rlGPuuAA");
client.AddDefaultHeader("Authorization", "Basic NzQ5YzBhZTRjMmNkY2RlNTZiMDIwOTY0YjZjYjcxMmY6"); //API Key 749c0ae4c2cdcde56b020964b6cb712f zakodowany w Base
client.Timeout = -1;
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);

i niestety znów 401. Natomiast z ciekawości puściłem takiego samego request'a na https://gtmetrix.com/api/2.0/reports/yIFECwVZ i otrzymałem już 200 OK

1

No dobra, dodanie tego na kliencie to był pomysł na szybko. Jak widać nie pomaga.

Trzeba poczytać te wątki na GitHubie i zobaczyć co proponują, tam się za bardzo nie wczytywałem ;)

0

@some_ONE: wielkie dzięki za naprowadzenie w czym był problem -> w czasie przekierowanie nie był przekazywany Header z kluczem API. Wreszcie dowiedziałem się czego w ogóle mam zacząć szukać. Bazując na Stackoverflow do kodu dodałem:

int numericStatusCode = (int)response.StatusCode;
if (numericStatusCode == 401)
{
  var redirectedClient = new RestClient(response.ResponseUri.ToString());
  redirectedClient.AddDefaultHeader("Authorization", "Basic NzQ5YzBhZTRjMmNkY2RlNTZiMDIwOTY0YjZjYjcxMmY6");
  IRestResponse newResponse = redirectedClient.Execute(request);
  Console.WriteLine(newResponse.Content);
}

i poszło :) Wielka prośba (jeśli oczywiście możesz) o informację na przyszłość w jakim programie śledziłeś ścieżki requesta (screen z niego wrzuciłeś w swoim poście)

1 użytkowników online, w tym zalogowanych: 0, gości: 1