Процедура входа в Discord OAuth с использованием PKCE - PullRequest
2 голосов
/ 16 февраля 2020

Я пытаюсь добавить Discord OAuth в мобильное приложение, используя PKCE . Я могу успешно пройти аутентификацию с помощью DiscordAPI и получаю обратно code с сервера.

Однако, когда я пытаюсь вызвать конечную точку https://discordapp.com/api/oauth2/token, я постоянно получаю Bad Request результат.

Я пробовал несколько подходов к получению моего токена, но все они потерпели неудачу.

Попытка (1) с использованием HttpClient:

var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("client_id", Constants.ClientId));
nvc.Add(new KeyValuePair<string, string>("client_secret", Constants.Secret));
nvc.Add(new KeyValuePair<string, string>("code_verifier", codeVerifier)); // method param
nvc.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("code", authCode)); // method param
nvc.Add(new KeyValuePair<string, string>("redirect_uri", Constants.Redirect));
nvc.Add(new KeyValuePair<string, string>("scope", "identify email connections"));

var req = new HttpRequestMessage(HttpMethod.Post, "https://discordapp.com/api/oauth2/token") 
{ 
    Content = new FormUrlEncodedContent(nvc) 
};
var authTokenResponse = await this.Client.SendAsync(req).ConfigureAwait(false);
// results in BadRequest

Я также пытался использовать StringContent объект, подобный этому:

var stringContent = new StringContent($"grant_type=authorization_code&client_id={Constants.ClientId}&client_secret={Constants.Secret}&code_verifier={codeVerifier}&code={authCode}&redirect_uri={Constants.Redirect}&scopes=identify%20email%20connections", Encoding.UTF8, "application/x-www-form-urlencoded");

Оба эти результата в Bad Request

Попытка (2) с использованием RestSharp

var client = new RestSharp.RestClient($"{this.Client.BaseAddress}/oauth2/token");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", $"grant_type=authorization_code&client_id={Constants.ClientId}&client_secret={Constants.Secret}&code_verifier={code}&code={authCode}&redirect_uri={Constants.Redirect}&scopes=identify%20email%20connections", ParameterType.RequestBody);
var response = await client.ExecuteAsync(request);

Я тщательно проверил все свои значения и удостоверился, что мой Client Id и Secret верны. Я пытался отправить запрос с и без code_verifier. Без code_verifier я получаю Unauthorized.

Вопрос

Как мне изменить мой запрос HttpClient для успешного получения моего токена аутентификации?

Дополнительная информация:

Чтобы соответствовать PKCE, мне нужен code_verifier. Вот как я генерирую код. (Обратите внимание, я не пытаюсь использовать это как код авторизации.)

Random rng = new Random();
byte[] code = new byte[32];
rng.NextBytes(code);

CodeVerifier = Base64UrlEncoder.Encode(rng.ToString());

Затем я использую алгоритм хэширования, заимствованный из c -sharpcorner.com , который выглядит следующим образом:

using (SHA256 sha256Hash = SHA256.Create())
{
    // ComputeHash - returns byte array  
    var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));

    // Convert byte array to a string   
    var builder = new StringBuilder();
    for (int i = 0; i < bytes.Length; i++)
    {
        builder.Append(bytes[i].ToString("x2"));
    }
    return builder.ToString();
}

Я передаю результат как code_challenge, получая свой код авторизации.

Вот где я узнал, что требуется PKCE.

https://github.com/discordapp/discord-api-docs/issues/450

Здесь продолжается обсуждение PKCE:

https://github.com/discordapp/discord-api-docs/issues/1296

Обновление:

Хорошо, поэтому, основываясь на ответе iwokal , я перечитал посты, указанные выше, так что PKCE "требуется" только если вы используете Custom Scheme Redirect, если вы просто с помощью перенаправления http просто оставьте code_verifier, и вы можете go.

При этом рабочий процесс PKCE недокументирован . Поскольку я потратил 500 баллов, я начисляю баллы тому, кто сможет решить рабочий процесс PKCE OAuth.

Вы можете приготовить решение с нуля, и я протестирую его.

Ответы [ 2 ]

3 голосов
/ 18 февраля 2020

Ну, после некоторого исследования я мог бы иметь представление о том, что происходит. Вы пытаетесь получить токен на основе созданного вами кода авторизации?

Я считаю, что правильным способом является отправка https://discordapp.com/api/oauth2/authorize запроса, который будет перенаправлен на предоставленный URL со строкой запроса, содержащей правильный код авторизации.

Вы должны использовать этот код на этапе получения токена.

Я не знаю, если это что-то меняет, но я считаю, что в документации нет такого параметра как "scopes" должно быть "область действия".

1 голос
/ 21 февраля 2020

Вы делаете запрос на неправильный URL. Поток кода создается для предотвращения утечки клиентского токена. Процесс выглядит следующим образом:

  1. Клиент запрашивает код с сервера разногласий
  2. Клиент отправляет этот код на ваш сервер
  3. Ваш сервер передает код в сервер Discord с его секретами
  4. Сервер Discord отвечает обратно на сконфигурированный redirect_uri, чтобы получить токен.

Примечание: есть 2 uri перенаправления, 1, который разрешает код и другой, который разрешает токен.

Вы пытаетесь сделать запрос напрямую к конечной точке токена. Вам необходимо сделать запрос к конечной точке авторизации. Для получения токена

https://discordapp.com/api/oauth2/authorize?response_type=code&client_id=157730590492196864&scope=identify%20guilds.join&state=15773059ghq9183habn&redirect_uri=https%3A%2F%2Fnicememe.website&prompt=consent

var state = Guid.NewGuid().ToString();
var codeParams = new List<KeyValuePair<string, string>>();
codeParams.Add(new KeyValuePair<string, string>("client_id", Constants.ClientId));
codeParams.Add(new KeyValuePair<string, string>("redirect_uri", Constants.Redirect));
codeParams.Add(new KeyValuePair<string, string>("scope", "identify email connections"));
codeParams.Add(new KeyValuePair<string, string>("response_type", "code"));
codeParams.Add(new KeyValuePair<string, string>("state", state))

var req = new HttpRequestMessage(HttpMethod.Post, 
"https://discordapp.com/api/oauth2/authorize") 
{ 
    Content = new FormUrlEncodedContent(nvc) 
};
var authTokenResponse = await this.Client.SendAsync(req).ConfigureAwait(false);

Discord затем ответит вашему redirect_uri с кодом и состоянием

https://mywebsite.com/?code=NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee&state=15773059ghq9183habn

Примечание. Исходный запрос на авторизацию не содержит ваш клиентский секрет, клиентский секрет используется только с вашего сервера , Таким образом, только сервер может запросить AuthTokens. Примечание. В большинстве мест, поддерживающих oauth, требуется настроить URL-адрес перенаправления с сервера.
Примечание. Состояние также будет возвращено с кодом, который вы проверяете. возвращено.

Возьмите код, который вам вернули (NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee), и используйте для запроса токена, используя исходный пример, с правильным кодом:

var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("client_id", Constants.ClientId));
nvc.Add(new KeyValuePair<string, string>("client_secret", Constants.Secret));
nvc.Add(new KeyValuePair<string, string>("code_verifier", codeVerifier)); // method param
nvc.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
nvc.Add(new KeyValuePair<string, string>("code", authCode)); // method param
nvc.Add(new KeyValuePair<string, string>("redirect_uri", Constants.Redirect));
nvc.Add(new KeyValuePair<string, string>("scope", "identify email connections"));

var req = new HttpRequestMessage(HttpMethod.Post,
"https://discordapp.com/api/oauth2/token") 
{ 
    Content = new FormUrlEncodedContent(nvc) 
};
var authTokenResponse = await this.Client.SendAsync(req).ConfigureAwait(false);

Обратите внимание, что когда вы делаете запрос к конечной точке токена, который вы используете

'Content-Type': 'application / x- www-form-urlencoded'

Если у вас все еще есть проблемы с потоком, Используйте Postman, который имеет встроенную поддержку OAuth, и получите токен, посмотрите, что они отправляют с помощью fiddler, и эмулируйте его.

...