Страницы

Поиск по вопросам

Показаны сообщения с ярлыком oauth. Показать все сообщения
Показаны сообщения с ярлыком oauth. Показать все сообщения

суббота, 11 апреля 2020 г.

Как получить имя и фамилию пользователя VK без дополнительного запроса?

#вконтакте #vkontakte_api #oauth

                    
Возник такой вопрос: сделать так, чтобы vk при авторизации через oauth 2, помимо
id, сразу возвращал имя и фамилию пользователя?
Попробовал в настройках указать первый запрос к api:
users.get?user_id={viewer_id}&v=5.25
Но, похоже, с oauth это не работает. Есть другие варианты, не делая дополнительный
запрос к api со стороны сервера, заставить vk возвращать вместе с id имя и фамилию?    


Ответы

Ответ 1



Я так понимаю, что приемлемо сделать правильный первый запрос к API, раз есть id пользователя: $.ajax({ url: "https://api.vk.com/method/users.get", data: { user_ids: '1', v: "5.26" }, dataType: "jsonp", success: function (e) {alert(e.response[0].first_name + ' ' + e.response[0].last_name);} });

вторник, 25 февраля 2020 г.

Алгоритм регистрации и авторизации через социальные сети

#oauth #php #vkontakte_api #авторизация #регистрация


Добрый вечер.
Расскажите, пожалуйста, как организовать сабж. Что хранить в базе, что держать в
куках и как организовать авторизацию. Можно без кода - на словах. На всякий случай
- пишу на PHP.
Заранее спасибо.    


Ответы

Ответ 1



Пришёл новый чел, авторизуете его через ВК (см. Виджет Авторизации), получая от ВК напрямую в ваш сервер подтверждение, что этот клиент действительно Вася с ID 12345. Генерите для Васи запись у себя в БД, в том числе некий уникальный ключ, а ему ставите куки с этим ключем. В следующий раз придёт - если есть куки, соотв. записи в вашей БД - это видимо, Вася.

Ответ 2



Воспользуйтесь уже готовыми решениями, например http://ulogin.ru/constructor.php На странице справки, в разделе "Настройки виджета" -> "Информация о пользователе" найдете все данные, которые можно выудить из соц сети про данного пользователя, от имени, почты, до пола и возраста (если конечно есть такие). Дальше - как при обычной авторизации через логин форму - ставите куку после авторизации, и понеслась. Или делаете что-то типа авторегистрации (минимально необходимые данные уже ведь есть) и если чего-то нехватает, можете перебрасывать пользователя на профиль и пусть дозаполняет то, что осталось в неведении. При этом, раз пользователь фактически не вводит пароль, то можно ему сгенерить какой-нибудь достаточно стойкий из вида "!k2j$4#(Dk)543\bls"

четверг, 23 января 2020 г.

С какого запроса следует начинать авторизацию на DropBox?

#http #https #oauth #request #dropbox


Доброго времени суток, уважаемые форумчане!

Изучаю документацию по протоколу Oauth 1.0 для DropBox, но не могу понять, с какого
запроса следует начинать сеанс обращения к серверу DropBox. В документации написано,
что первый запрос должен содержать следующие параметры:


  
  oauth_consumer_key: The Consumer Key.
  oauth_signature_method:
  The signature method the Consumer used to sign the request.
  oauth_signature:
  The signature as defined in Signing Requests.
  oauth_timestamp:
  As defined in Nonce and Timestamp.
  oauth_nonce:
  As defined in Nonce and Timestamp.
  oauth_version:
  OPTIONAL. If present, value MUST be 1.0 . Service Providers MUST assume the protocol
version to be 1.0 if this parameter is not present. Service Providers’ response to
non-1.0 value is left undefined.
  Additional parameters:
  Any additional parameters, as defined by the Service Provider.
  


Для вычисления подписи предлагается использовать PLAINTEXT, HMAC-SHA1 или RSA-SHA1.
Методом PLAINTEXT ничего не получилось, как не пыталась отправлять запросы, сервер
всё время возвращал ошибку "Not authorized".

Решила попробовать создавать подпись с помощью HMAC-SHA1, но не понятно, что будет
являться входным параметром, а что ключом. Подскажите ещё, пожалуйста, какой нужно
отправлять метод: POST или GET, куда передавать параметры: в строку запроса или в тело
запроса?

Ссылка на рефы DropBox: http://oauth.net/core/1.0/#signing_process

Пример:

    wstring url = L"https://www.dropbox.com/home/request_token";
    wstring oauth_nonce;
    CreateRequestParam(oauth_nonce);
    wstring oauth_callback = L"";
    wstring oauth_consumer_key = L"my App key";
    wstring oauth_consumer_secret = L"my App secret";
    wstring oauth_signature_method = L"PLAINTEXT";
    wstring oauth_timestamp = to_wstring(*(double*)time);
    wstring oauth_version = L"1.0";
    wstring oauth_signature = oauth_consumer_secret + L"&";
    wstring params = L"oauth_consumer_key=" + oauth_consumer_key +
                     L"&oauth_signature_method=" + oauth_signature_method +
                     L"&oauth_signature=" + oauth_signature +
                     L"&oauth_timestamp=" + oauth_timestamp +
                     L"&oauth_nonce=" + oauth_nonce +
                     L"&oauth_version=" + oauth_version;

    int err = PostRequest(url, params);


На указанный url отправляю HTTP POST запрос, в теле передаю параметры, указанные
выше. Должны возвращаться out_token и outh_secret, но возвращается страница в html-формате,
в которой нет ни того, ни другого. Что я делаю не так?
    


Ответы

Ответ 1



Не знаю, актуален ли еще вопрос, попробую ответить. Первый запрос на адрес https://api.dropbox.com/1/oauth/request_token Пример: POST https://api.dropbox.com/1/oauth/request_token HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0 Host: api.dropbox.com Accept: */* Accept-Encoding: deflate, gzip Authorization: OAuth oauth_consumer_key="973quph3jxdgqoe",oauth_nonce="105816471209",oauth_signature="wloizpn331cc8zd&",oauth_signature_method="PLAINTEXT",oauth_timestamp="Wed%2C%2013%20May%202015%2015%3A37%3A53%20%2B0000",oauth_version="1.0" Content-Length: 0 Content-Type: application/x-www-form-urlencoded В ответ получаете oauth_token HTTP/1.1 200 OK Server: nginx Date: Wed, 13 May 2015 15:46:32 GMT Content-Type: application/x-www-form-urlencoded ... Content-Length: 64 oauth_token_secret=LZl8HyVRQuP1ifON&oauth_token=RS4XReiBykw8WHNz Затем отправляете пользователя в браузер на страничку https://www.dropbox.com/1/oauth/authorize?oauth_token=RS4XReiBykw8WHNz Пользователь нажимает кнопочку Allow. Затем выполняете запрос на второй URL (подписываете его уже с использованием полученного ранее oauth_token_secret: https://api.dropbox.com/1/oauth/access_token POST https://api.dropbox.com/1/oauth/access_token HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0 Host: api.dropbox.com Accept: */* Accept-Encoding: deflate, gzip Authorization: OAuth oauth_consumer_key="973quph3jxdgqoe",oauth_nonce="847334669",oauth_signature="wloizpn331cc8zd&LZl8HyVRQuP1ifON",oauth_signature_method="PLAINTEXT",oauth_timestamp="Wed%2C%2013%20May%202015%2015%3A46%3A46%20%2B0000",oauth_token="RS4XReiBykw8WHNz",oauth_version="1.0" Content-Length: 0 Content-Type: application/x-www-form-urlencoded И получаете в ответ токен, который будете использовать при последующих запросах: HTTP/1.1 200 OK Server: nginx Date: Wed, 13 May 2015 15:46:55 GMT Content-Type: application/x-www-form-urlencoded Connection: keep-alive ... Content-Length: 76 oauth_token_secret=32mdalirxxxxa8j&oauth_token=yx8f9rdxxxxj8dia&uid=1921xxxx Пример авторизации на Dropbox Пример OAuth авторизации на Flickr с подписыванием запроса HMAC-SHA1

четверг, 9 января 2020 г.

Различия OAuth и OpenID

#авторизация #oauth #аутентификация #oauth2 #openid


Скажите, пожалуйста, чем отличаются OAuth и OpenID?
    


Ответы

Ответ 1



OpenID позволяет сайту удостовериться, что его пользователь владеет неким персональным URL (своим сайтом, блогом, профилем). Этого факта достаточно для того, чтобы использовать уникальный URL для узнавания того же самого пользователя в следующий раз. И всё. Все остальные вещи — заведение аккаунта, получение email'а и других данных, разрешение какой-то активности на сайте — остаётся на усмотрение сайта. Другими словами, OpenID — это чистая аутентификация: вы знаете, кто к вам пришёл, но вольны делать с этим знанием всё, что угодно. OAuth позволяет программе (на вебе или локальной) получить от пользователя права на использование какого-то конкретного API. Права обозначаются токеном, свойства которого никак не определены: он может быть одинаковым для разных пользователей, может быть разным для одного в разное время. Всё, что гарантируется — это что программа в обмен на токен сможет выполнять какие-то действия на каком-то сервисе. Другими словами, OAuth — это чистая авторизация: вы обладаете конкретными правами, но не можете в общем случае по ним определить, кому они принадлежат. Аналогия. OpenID — это ваш паспорт: он говорит, кто вы, но что он даёт, зависит от места, куда вы с ним пришли. OAuth — ключи от вашей машины: с ними можно ездить на вашей машине, даже не зная, как вас зовут. Разница между OpenID и OAuth - Иван Сагалаев

воскресенье, 22 декабря 2019 г.

реализация ЕСИА на сайте

#c_sharp #авторизация #oauth #аутентификация


Добрый день! Встал вопрос прикрутить авторизацию на сайте через есиа (Госуслуги). 
Наверняка кто-то уже проделывал это! Помогите пожалуйста, я от этого очень далека,
а сделать надо...

В общем, имеется сайт на ASP.NET (c#, Windows Forms).

Выбранный тип аутентификации - OAuth 2.0 и OpenID Connect 1.0.

Уже, как указано, на сайте минкомсвязи сформировали заявку на подключение к тестовой
среде ЕСИА. Заявку одобрили, прислали документ с инструкцией и 3 файла. В инструкии
как таковых шагов нет, только логины и пароли для входа на госуслуги тестовые и описание
того, что должно прийти в ответ. 

Файлы типа .rar - по инструкции это якобы контейнеры с ключами для ЭЦП.
например, архив EsiaTest.006.rar содержит такие файлы:


header.key 
masks.key
masks2.key
name.key
primary.key
primary2.key


Все параметры для авторизации в принципе понятны, кроме как раз подписи.
Вот как раз с формированием ЭЦП у меня проблемы.

В документе сказано формировать ее так:


  Подпись запроса в формате PKCS#7 detached signature в кодировке UTF-8 от значений
четырех параметров HTTP–запроса: scope, timestamp, clientId, state (они у меня есть).
Подпись должна быть закодирована в формате base64 url safe. Используемый для проверки
подписи сертификат должен быть предварительно зарегистрирован в ЕСИА и привязан к учетной
записи системы-клиента в ЕСИА. ЕСИА поддерживает сертификаты в формате X.509. ЕСИА
поддерживает алгоритмы формирования электронной подписи RSA с длиной ключа 2048 и алгоритмом
криптографического хэширования SHA-256, а также алгоритм электронной подписи ГОСТ Р
34.10-2001 и алгоритм криптографического хэширования ГОСТ Р 34.11-94.


Помогите сформировать эту подпись! Как все это реализовать на c# ?
Заранее благодарю
    


Ответы

Ответ 1



Шаги по интеграции вы вряд ли где-то найдете. Есть только методические рекомендации и регламент работы с ЕСИА, но адекватного описания процедуры "как это сделать на C#" там нет :) То, что вам прислали вместе с ответом на заявку — это просто примеры разных тестовых аккаунтов для отладки работы, т.е. это НИКАК НЕ СВЯЗАНО с подписью ваших запросов к ЕСИА. Вот команды которыми мы генерирует правильный сертификат: openssl req -nodes -sha256 -newkey rsa:2048 -keyout secret.key -out request.csr -subj /C=RU/ST=Rostov-na-Donu/L=city/O=COMPANY\/emailAddress=EMAIL@site.ru/ openssl x509 -req -sha256 -days 3650 -in request.csr -signkey secret.key -out cert.crt -extfile /etc/ssl/openssl.cnf -extensions v3_ca openssl x509 -in cert.crt -text Соответственно в нужные места нужно подставить правильные данные о компании почту и т.п. это команда работает под линуксом при этом в файле /etc/ssl/openssl.cnf надо добавить(изменить) следующую секцию: [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment extendedKeyUsage = emailProtection, clientAuth subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer в результате вы получите работающий сертификат cert.crt и ключ secret.key Но это будет только начало. Там еще вагон и маленькая тележка нюансов по части обмена данными с ЕСИА. Вот тут есть готовое решение, в т.ч. и на C#: esia.pro Оно платное, но помимо готовой реализации там еще проконсультируют по процессу интеграции и оргвопросам.

Ответ 2



У нас подпись заработала как-то так: public static byte[] Sign(X509Certificate2 certificate, byte[] data) { if (data == null) throw new ArgumentNullException("data"); if (certificate == null) throw new ArgumentNullException("certificate"); // setup the data to sign ContentInfo content = new ContentInfo(data); SignedCms signedCms = new SignedCms(content, true); CmsSigner signer = new CmsSigner(certificate); var sha256 = new Oid("2.16.840.1.101.3.4.2.1", "sha256"); signer.DigestAlgorithm = sha256; // create the signature signedCms.ComputeSignature(signer); var signature = signedCms.Encode(); return signature; } С base64 там всё не очевидно: Convert.ToBase64String не подходит, не достаточно точно реализует стандарт. Использовал вот такой метод: public static string Base64UrlEncode(byte[] arg) { string s = Convert.ToBase64String(arg); // Regular base64 encoder s = s.Split('=')[0]; // Remove any trailing '='s s = s.Replace('+', '-'); // 62nd char of encoding s = s.Replace('/', '_'); // 63rd char of encoding return s; } Проверка их подписи - это очередной ребус. Можно предположить, что это процесс обратный процессу подписания, т.е. как-то так: public static bool ValidateCmsSignature(byte[] data, byte[] signature, X509Certificate2 certificate) { bool result = false; if (data == null) throw new ArgumentNullException("data"); if (signature == null) throw new ArgumentNullException("signature"); if (certificate == null) throw new ArgumentNullException("certificate"); // setup the data to sign ContentInfo content = new ContentInfo(data); SignedCms signedCms = new SignedCms(content, true); try { signedCms.Decode(signature); signedCms.CheckSignature(new X509Certificate2Collection(certificate), true); result = true; } catch (Exception ex) { var msg = ex.Message; } return result; } И этот метод работает для проверки нашей подписи, но не подходит для их подписи. А почему? Если мы сначала формируем текстовую строку, потом создаем отсоединенную подпись по ней, и потом всё это кодируем в base64, то они формируют два JSON'а: HEADER и PAYLOAD, потом кодируют их по отдельности в base64, и вот потом формируют подпись по строке HEADERbase64.PAYLOADbase64. А потом уже и подпись кодируют в base64. Итого, метод для проверки их подписи выглядит как-то так: public static bool ValidateSignature(byte[] data, byte[] signature, X509Certificate2 certificate) { bool result = false; using (var csp = (RSACryptoServiceProvider) certificate.PublicKey.Key) { using (var hasher = new SHA256Managed()) { var hash = hasher.ComputeHash(data); string id = CryptoConfig.MapNameToOID("SHA256"); bool isDataok = csp.VerifyData(data, id, signature); bool isHashOk = csp.VerifyHash(hash, id, signature); // можно ещё так //RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(csp); //rsaDeformatter.SetHashAlgorithm("SHA256"); //bool isHashOk2 = rsaDeformatter.VerifySignature(hash, signature); result = isDataok && isHashOk; } } return result; }

Ответ 3



А вообще уже есть в NuGet полезнейшая библиотечка, с которой всё становится легче: https://www.nuget.org/packages?q=esia.net В наши времена первопроходимцев так не баловали :))

воскресенье, 8 декабря 2019 г.

Как правильно “готовить” авторизацию в SPA?

#c_sharp #aspnet_core #aspnet_web_api #oauth #jwt


Цель такая: написать бэкенд ASP.Net Core MVC* SPA для работы с ReactJS и дальнейшей
возможностью переиспользовать существующий API для создания, скажем, Android приложения.  
Платформа: .Net Core 2.1.

* - Если такое ещё можно назвать MVC, учитывая, что View не будет, а будет отдельная
директория ClientApp со всем содержимым фронтэнда.

Пошуршав интернеты, наткнулся на то, что ASP.Net Core Identity не актуален. Звучит
логично, учитывая, что тот сильно опирается на куки, а при общении через API куки таскать
неудобно. Хотя многие примеры нижеупомянутого JWT всё же используют IdentityUser.

Много инструкций с использованием JWT. Приличная часть из них слишком зациклена на
фронтенд реализации и практически ничего не говорит о бэкенде. Не нашёл примеров с
OAuth2, везде свой велосипед, причём Demo и не пригодный для реального использования. 
В тех же примерах по JWT используются Issuer, Audience и SecretKey, но ни слова о
том, по каким правилам их надо выбирать и/или генерировать, ну и где безопасно хранить
(если исключить примеры с хардкодом, то их обычно хранили в appsettings.json).

Также в процессе гугления (конкретно: попытке найти инфу об JwtSecurityTokenHandler
из System.IdentityModel.Tokens) MSDN Microsoft выдаёт:


  We’re no longer updating this content regularly. Check the Microsoft Product Lifecycle
for information about how this product, service, or technology is supported.


Что это значит? Microsoft более не поддерживает JWT? Технология в принципе уже неактуальна?
Что же тогда использовать?

В итоге мне теперь не совсем понятно как строить авторизацию в своём приложении:


Нужные ли мне IdentityUser из Microsoft.AspNetCore.Identity?
Нужен ли мне IdentityDbContext из Microsoft.AspNetCore.Identity.EntityFrameworkCore?
Актуален ли JWT?
Как построить авторизацию с OAuth2?


Хотелось бы сохранить доступность авторизованного пользователя из HttpContext.User
и к его Claims, чтобы не мучать БД лишний раз для получения Id/UserName/Avatar/LastOnline/etc. 

А также учесть то, что активно будут использоваться роли пользователей. 

UPD: Немного обновлю конечные цели, чтобы стало понятнее:


Это форум с разделами, постами и комментариями в формате вопрос-ответ 
Реализация фронтенда через SPA
Планируется открытое API
Это же API будет использовать SPA
Поддержка быстрой регистрации/входа через сторонние сервисы (Vk, FB, Google, etc.)

    


Ответы

Ответ 1



Вот проверенный код, который работает. Итак, настройка аутентификации в Startup public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); services.AddDbContext(options => options.UseMySql(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity() .AddEntityFrameworkStores(); services.Configure(options => { //........ }); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { // укзывает, будет ли валидироваться издатель при валидации токена ValidateIssuer = false, // будет ли валидироваться потребитель токена ValidateAudience = false, // будет ли валидироваться время существования ValidateLifetime = true, // установка ключа безопасности IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(), // валидация ключа безопасности ValidateIssuerSigningKey = true, }; }); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); }); // ......... } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ............... app.UseAuthentication(); // ................ } } Класс AuthOptions public class AuthOptions { const string KEY = "mysupersecret_secretkey!"; // ключ для шифрации public const int LIFETIME = 300; // время жизни токена - 5 часов public static SymmetricSecurityKey GetSymmetricSecurityKey() { return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KEY)); } } Моделька для запроса токена public class TokenRequest { public string UserName { get; set; } public string Password { get; set; } } Контроллер для получения токена [Route("api/[controller]")] public class AccountController : Controller { private readonly UserManager _userManager; private readonly SignInManager _signInManager; public AccountController(UserManager userManager, SignInManager signInManager) { _userManager = userManager; _signInManager = signInManager; } [HttpPost("[action]")] public async Task Auth([FromBody] TokenRequest tokenRequest) { var username = tokenRequest.UserName; var password = tokenRequest.Password; var principal = await GetPrincipal(username, password); if (principal == null) { return StatusCode(400, "Invalid username or password."); } var now = DateTime.UtcNow; // создаем JWT-токен var jwt = new JwtSecurityToken( notBefore: now, claims: principal.Claims, expires: now.Add(TimeSpan.FromMinutes(AuthOptions.LIFETIME)), signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var response = new { token = encodedJwt, username = principal.Identity.Name }; return Json(response); } private async Task GetPrincipal(string username, string password) { var user = await _userManager.FindByNameAsync(username); if (user != null) { var check = await _userManager.CheckPasswordAsync(user, password); if (check) { var principal = await _signInManager.CreateUserPrincipalAsync(user); return principal; } } return null; } } И, по моему, это все. С этим все остальные вещи типа атрибутов авторизации, роли и прочее работает из коробки. Насколько я помню, ничего дополнительно делать не надо.

Ответ 2



Для начала, ASP.Net Core Identity никаким образом не опирается на куки! ASP.Net Core Identity - это прежде всего система для хранения и обработки информации о пользователях. Все что делает Identity во время аутентификации - это загружает из БД информацию о пользователе, заполняя набор утверждений о нем, после чего передает его ASP.NET Core. Соответственно, нет никакого смысла искать как использовать ASP.Net Core Identity вместе с Jwt - это попросту не связанные друг с другом вещи. Теперь про OAuth. Технология OAuth предназначена для того, чтобы сторонний разработчик мог создать приложение которое бы работало с вашим сайтом, но при этом не имел доступа к паролям ваших пользователей. Если же вы собрались создавать приложение на Андроиде самостоятельно - использование OAuth будет для вас избыточно! Все что вам нужно - это action в контроллере, который принимает логин с паролем и выдает токен. Ну а если вы решили дать доступ к своему API именно для сторонних разработчиков - Identity Server вам в помощь. Наконец, про JWT. Audience - это параметр токена, который означает целевую систему куда его можно предъявлять. Иными словами, это URL вашего сайта. Однако, нет никакого смысла в его использовании в ситуации когда у вас только один сайт: он нужен когда есть несколько сайтов со своими API и эти сайты не доверяют друг другу. Issuer - это URI провайдера, который выдал JWT. То есть, опять-таки, URL вашего сайта. Это поле используется только в тех случаях когда доступ к API можно получить при помощи токенов от разных поставщиков. В остальных случаях это поле вообще-то не обязательное. SecretKey - это, скорее всего, ключ, которым вы шифруете или подписываете JWT. Хранить его можно в appsettings.json или в переменных окружения. Главное - не публикуйте его на github. Вообще, желательно использовать разные ключи для разработки и на "боевом" сервере.

Ответ 3



Отталкивался я от ответа @tym32167 (поэтому и пометил его как правильный), но на всякий случай добавлю конкретно свою реализацию. Вдруг будет кому-то полезна. Startup.cs public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { // подключение DbContext'ов // подключение Identity services.Configure(options => { // ... }); // игнорирование регистра (url без заглавных букв) services.Configure(options => options.LowercaseUrls = true); // подключение репозиториев services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false, //ValidIssuer = "", ValidateAudience = false, //ValidAudience = "", ValidateLifetime = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["Jwt:Key"])), ValidateIssuerSigningKey = true, }; }); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); }); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/build"; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); //ToDo: do it better app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseSpaStaticFiles(); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action=Index}/{id?}"); }); app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; if (env.IsDevelopment()) { // актуально для ReactJS spa.UseReactDevelopmentServer(npmScript: "start"); } }); } } appsettings.json { "Jwt": { "Key": "my_very_secret_key", "LifeTime": "300" } } Jwt:Key должен быть больше 16 символов (128 бит) иначе выбрасывает исключение при генерации токена. По крайней мере, при использовании шифрования HmacSha256. TokenRequest.cs public class TokenRequest { [Required(ErrorMessage = "")] [JsonProperty("login")] public string Login { get; set; } [Required(ErrorMessage = "")] [DataType(DataType.Password)] [JsonProperty("password")] public string Password { get; set; } } RegisterModel.cs public class RegisterModel { [Required(ErrorMessage = "")] [JsonProperty("username")] public string UserName { get; set; } [Required(ErrorMessage = "")] [EmailAddress] [DataType(DataType.EmailAddress)] [JsonProperty("email")] public string Email { get; set; } [Required(ErrorMessage = "")] [DataType(DataType.Password)] [JsonProperty("password")] public string Password { get; set; } [DataType(DataType.Password)] [Compare("Password", ErrorMessage = "")] [JsonProperty("confirm_password")] public string ConfirmPassword { get; set; } } AccountController.cs [Route("api/[controller]")] [ApiController] public class AccountController : ControllerBase { private readonly UserManager userManager; private readonly SignInManager signInManager; private readonly IConfiguration configuration; public AccountController( UserManager userManager, SignInManager signInManager, IConfiguration configuration) { this.userManager = userManager; this.signInManager = signInManager; this.configuration = configuration; } [HttpPost("[action]")] public async Task Register([FromBody] RegisterModel model) { if (!ModelState.IsValid) { var errors = ModelState.Values.SelectMany(v => v.Errors.Select(e => e.ErrorMessage)); return BadRequest(errors); } var userAccount = new UserAccount { Email = model.Email, UserName = model.UserName }; var result = await userManager.CreateAsync(userAccount, model.Password); if (result.Succeeded) { var token = await GetTokenAsync(userAccount); return Ok(new { token }); } return BadRequest(result.Errors.Select(e => e.Description)); } [HttpPost("[action]")] public async Task Auth([FromBody] TokenRequest tokenRequest) { var userAccount = tokenRequest.Login.Contains('@') ? await userManager.FindByEmailAsync(tokenRequest.Login) : await userManager.FindByNameAsync(tokenRequest.Login); if (userAccount == null) { return NotFound(new { message = $"User with login {tokenRequest.Login} not found!" }); } var passValided = await userManager.CheckPasswordAsync(userAccount, tokenRequest.Password); if (!passValided) { return UnprocessableEntity(new { message = "Invalid username or password." }); } var token = await GetTokenAsync(userAccount); return Ok(token); } private async Task GetTokenAsync(UserAccount userAccount) { var principal = await signInManager.CreateUserPrincipalAsync(userAccount); var identity = (ClaimsIdentity)principal.Identity; if (identity == null) { return null; } var now = DateTime.UtcNow; // created JWT-token var jwt = new JwtSecurityToken( notBefore: DateTime.UtcNow, claims: identity.Claims, expires: now.Add(TimeSpan.FromMinutes(int.Parse(configuration["Jwt:LifeTime"]))), signingCredentials: new SigningCredentials( new SymmetricSecurityKey( Encoding.ASCII.GetBytes(configuration["Jwt:Key"])), SecurityAlgorithms.HmacSha256)); var token = new JwtSecurityTokenHandler().WriteToken(jwt); return token; } } UserAccount наследник IdentityUser, а UserRole наследник IdentityRole

вторник, 14 мая 2019 г.

Алгоритм регистрации и авторизации через социальные сети

Добрый вечер. Расскажите, пожалуйста, как организовать сабж. Что хранить в базе, что держать в куках и как организовать авторизацию. Можно без кода - на словах. На всякий случай - пишу на PHP. Заранее спасибо.


Ответ

Пришёл новый чел, авторизуете его через ВК (см. Виджет Авторизации), получая от ВК напрямую в ваш сервер подтверждение, что этот клиент действительно Вася с ID 12345. Генерите для Васи запись у себя в БД, в том числе некий уникальный ключ, а ему ставите куки с этим ключем. В следующий раз придёт - если есть куки, соотв. записи в вашей БД - это видимо, Вася.

суббота, 16 марта 2019 г.

С какого запроса следует начинать авторизацию на DropBox?

Доброго времени суток, уважаемые форумчане!
Изучаю документацию по протоколу Oauth 1.0 для DropBox, но не могу понять, с какого запроса следует начинать сеанс обращения к серверу DropBox. В документации написано, что первый запрос должен содержать следующие параметры:
oauth_consumer_key: The Consumer Key. oauth_signature_method: The signature method the Consumer used to sign the request. oauth_signature: The signature as defined in Signing Requests. oauth_timestamp: As defined in Nonce and Timestamp. oauth_nonce: As defined in Nonce and Timestamp. oauth_version: OPTIONAL. If present, value MUST be 1.0 . Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. Service Providers’ response to non-1.0 value is left undefined. Additional parameters: Any additional parameters, as defined by the Service Provider.
Для вычисления подписи предлагается использовать PLAINTEXT, HMAC-SHA1 или RSA-SHA1. Методом PLAINTEXT ничего не получилось, как не пыталась отправлять запросы, сервер всё время возвращал ошибку "Not authorized".
Решила попробовать создавать подпись с помощью HMAC-SHA1, но не понятно, что будет являться входным параметром, а что ключом. Подскажите ещё, пожалуйста, какой нужно отправлять метод: POST или GET, куда передавать параметры: в строку запроса или в тело запроса?
Ссылка на рефы DropBox: http://oauth.net/core/1.0/#signing_process
Пример:
wstring url = L"https://www.dropbox.com/home/request_token"; wstring oauth_nonce; CreateRequestParam(oauth_nonce); wstring oauth_callback = L""; wstring oauth_consumer_key = L"my App key"; wstring oauth_consumer_secret = L"my App secret"; wstring oauth_signature_method = L"PLAINTEXT"; wstring oauth_timestamp = to_wstring(*(double*)time); wstring oauth_version = L"1.0"; wstring oauth_signature = oauth_consumer_secret + L"&"; wstring params = L"oauth_consumer_key=" + oauth_consumer_key + L"&oauth_signature_method=" + oauth_signature_method + L"&oauth_signature=" + oauth_signature + L"&oauth_timestamp=" + oauth_timestamp + L"&oauth_nonce=" + oauth_nonce + L"&oauth_version=" + oauth_version;
int err = PostRequest(url, params);
На указанный url отправляю HTTP POST запрос, в теле передаю параметры, указанные выше. Должны возвращаться out_token и outh_secret, но возвращается страница в html-формате, в которой нет ни того, ни другого. Что я делаю не так?


Ответ

Не знаю, актуален ли еще вопрос, попробую ответить.
Первый запрос на адрес https://api.dropbox.com/1/oauth/request_token
Пример:
POST https://api.dropbox.com/1/oauth/request_token HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0 Host: api.dropbox.com Accept: */* Accept-Encoding: deflate, gzip Authorization: OAuth oauth_consumer_key="973quph3jxdgqoe",oauth_nonce="105816471209",oauth_signature="wloizpn331cc8zd&",oauth_signature_method="PLAINTEXT",oauth_timestamp="Wed%2C%2013%20May%202015%2015%3A37%3A53%20%2B0000",oauth_version="1.0" Content-Length: 0 Content-Type: application/x-www-form-urlencoded
В ответ получаете oauth_token
HTTP/1.1 200 OK Server: nginx Date: Wed, 13 May 2015 15:46:32 GMT Content-Type: application/x-www-form-urlencoded ... Content-Length: 64
oauth_token_secret=LZl8HyVRQuP1ifON&oauth_token=RS4XReiBykw8WHNz
Затем отправляете пользователя в браузер на страничку https://www.dropbox.com/1/oauth/authorize?oauth_token=RS4XReiBykw8WHNz
Пользователь нажимает кнопочку Allow.
Затем выполняете запрос на второй URL (подписываете его уже с использованием полученного ранее oauth_token_secret
https://api.dropbox.com/1/oauth/access_token
POST https://api.dropbox.com/1/oauth/access_token HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0 Host: api.dropbox.com Accept: */* Accept-Encoding: deflate, gzip Authorization: OAuth oauth_consumer_key="973quph3jxdgqoe",oauth_nonce="847334669",oauth_signature="wloizpn331cc8zd&LZl8HyVRQuP1ifON",oauth_signature_method="PLAINTEXT",oauth_timestamp="Wed%2C%2013%20May%202015%2015%3A46%3A46%20%2B0000",oauth_token="RS4XReiBykw8WHNz",oauth_version="1.0" Content-Length: 0 Content-Type: application/x-www-form-urlencoded
И получаете в ответ токен, который будете использовать при последующих запросах:
HTTP/1.1 200 OK Server: nginx Date: Wed, 13 May 2015 15:46:55 GMT Content-Type: application/x-www-form-urlencoded Connection: keep-alive ... Content-Length: 76
oauth_token_secret=32mdalirxxxxa8j&oauth_token=yx8f9rdxxxxj8dia&uid=1921xxxx
Пример авторизации на Dropbox
Пример OAuth авторизации на Flickr с подписыванием запроса HMAC-SHA1

понедельник, 18 февраля 2019 г.

Различия OAuth и OpenID

Скажите, пожалуйста, чем отличаются OAuth и OpenID?


Ответ

OpenID позволяет сайту удостовериться, что его пользователь владеет неким персональным URL (своим сайтом, блогом, профилем). Этого факта достаточно для того, чтобы использовать уникальный URL для узнавания того же самого пользователя в следующий раз. И всё. Все остальные вещи — заведение аккаунта, получение email'а и других данных, разрешение какой-то активности на сайте — остаётся на усмотрение сайта. Другими словами, OpenID — это чистая аутентификация: вы знаете, кто к вам пришёл, но вольны делать с этим знанием всё, что угодно.

OAuth позволяет программе (на вебе или локальной) получить от пользователя права на использование какого-то конкретного API. Права обозначаются токеном, свойства которого никак не определены: он может быть одинаковым для разных пользователей, может быть разным для одного в разное время. Всё, что гарантируется — это что программа в обмен на токен сможет выполнять какие-то действия на каком-то сервисе. Другими словами, OAuth — это чистая авторизация: вы обладаете конкретными правами, но не можете в общем случае по ним определить, кому они принадлежат.

Аналогия. OpenID — это ваш паспорт: он говорит, кто вы, но что он даёт, зависит от места, куда вы с ним пришли. OAuth — ключи от вашей машины: с ними можно ездить на вашей машине, даже не зная, как вас зовут.
Разница между OpenID и OAuth - Иван Сагалаев

четверг, 18 октября 2018 г.

Как правильно “готовить” авторизацию в SPA?

Цель такая: написать бэкенд ASP.Net Core MVC* SPA для работы с ReactJS и дальнейшей возможностью переиспользовать существующий API для создания, скажем, Android приложения. Платформа: .Net Core 2.1
* - Если такое ещё можно назвать MVC, учитывая, что View не будет, а будет отдельная директория ClientApp со всем содержимым фронтэнда.
Пошуршав интернеты, наткнулся на то, что ASP.Net Core Identity не актуален. Звучит логично, учитывая, что тот сильно опирается на куки, а при общении через API куки таскать неудобно. Хотя многие примеры нижеупомянутого JWT всё же используют IdentityUser
Много инструкций с использованием JWT. Приличная часть из них слишком зациклена на фронтенд реализации и практически ничего не говорит о бэкенде. Не нашёл примеров с OAuth2, везде свой велосипед, причём Demo и не пригодный для реального использования. В тех же примерах по JWT используются Issuer, Audience и SecretKey, но ни слова о том, по каким правилам их надо выбирать и/или генерировать, ну и где безопасно хранить (если исключить примеры с хардкодом, то их обычно хранили в appsettings.json).
Также в процессе гугления (конкретно: попытке найти инфу об JwtSecurityTokenHandler из System.IdentityModel.Tokens) MSDN Microsoft выдаёт:
We’re no longer updating this content regularly. Check the Microsoft Product Lifecycle for information about how this product, service, or technology is supported.
Что это значит? Microsoft более не поддерживает JWT? Технология в принципе уже неактуальна? Что же тогда использовать?
В итоге мне теперь не совсем понятно как строить авторизацию в своём приложении:
Нужные ли мне IdentityUser из Microsoft.AspNetCore.Identity? Нужен ли мне IdentityDbContext из Microsoft.AspNetCore.Identity.EntityFrameworkCore? Актуален ли JWT? Как построить авторизацию с OAuth2?
Хотелось бы сохранить доступность авторизованного пользователя из HttpContext.User и к его Claims, чтобы не мучать БД лишний раз для получения Id/UserName/Avatar/LastOnline/etc.
А также учесть то, что активно будут использоваться роли пользователей.
UPD: Немного обновлю конечные цели, чтобы стало понятнее:
Это форум с разделами, постами и комментариями в формате вопрос-ответ Реализация фронтенда через SPA Планируется открытое API Это же API будет использовать SPA Поддержка быстрой регистрации/входа через сторонние сервисы (Vk, FB, Google, etc.)


Ответ

Вот проверенный код, который работает. Итак, настройка аутентификации в Startup
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
services.AddDbContext(options => options.UseMySql(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity() .AddEntityFrameworkStores();
services.Configure(options => { //........ });
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.TokenValidationParameters = new TokenValidationParameters { // укзывает, будет ли валидироваться издатель при валидации токена ValidateIssuer = false, // будет ли валидироваться потребитель токена ValidateAudience = false, // будет ли валидироваться время существования ValidateLifetime = true,
// установка ключа безопасности IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(), // валидация ключа безопасности ValidateIssuerSigningKey = true, }; });
services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .Build(); });
// ......... }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ............... app.UseAuthentication(); // ................ } }
Класс AuthOptions
public class AuthOptions { const string KEY = "mysupersecret_secretkey!"; // ключ для шифрации public const int LIFETIME = 300; // время жизни токена - 5 часов public static SymmetricSecurityKey GetSymmetricSecurityKey() { return new SymmetricSecurityKey(Encoding.ASCII.GetBytes(KEY)); } }
Моделька для запроса токена
public class TokenRequest { public string UserName { get; set; } public string Password { get; set; } }
Контроллер для получения токена
[Route("api/[controller]")] public class AccountController : Controller { private readonly UserManager _userManager; private readonly SignInManager _signInManager;
public AccountController(UserManager userManager, SignInManager signInManager) { _userManager = userManager; _signInManager = signInManager; }
[HttpPost("[action]")] public async Task Auth([FromBody] TokenRequest tokenRequest) { var username = tokenRequest.UserName; var password = tokenRequest.Password;
var principal = await GetPrincipal(username, password); if (principal == null) { return StatusCode(400, "Invalid username or password."); }
var now = DateTime.UtcNow; // создаем JWT-токен var jwt = new JwtSecurityToken( notBefore: now, claims: principal.Claims, expires: now.Add(TimeSpan.FromMinutes(AuthOptions.LIFETIME)), signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256)); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new { token = encodedJwt, username = principal.Identity.Name };
return Json(response); }
private async Task GetPrincipal(string username, string password) { var user = await _userManager.FindByNameAsync(username); if (user != null) { var check = await _userManager.CheckPasswordAsync(user, password); if (check) { var principal = await _signInManager.CreateUserPrincipalAsync(user); return principal; } } return null; } }
И, по моему, это все. С этим все остальные вещи типа атрибутов авторизации, роли и прочее работает из коробки. Насколько я помню, ничего дополнительно делать не надо.

понедельник, 1 октября 2018 г.

Принцип работы Oauth2

Здравствуйте, сразу прошу извинить если вопрос глупый. Собственно сейчас разбираюсь с возможными вариантами авторизации с помощью REST API. Сейчас все социальные сети используют авторизацию OAuth/OAuth2. Прочитав немало статьей, все равно не очень хорошо отложилось в голове и есть много сомнений, что правильно понимаю.
Первое что это главные игроки, я не буду называть как в документации сказано, назову как понимаю.
Пользователь. Это владелец аккаунта который имеет доступ к каким-то ресурсам. Например Facebook/Twitter. Сервер авторизации OAuth/OAuth2. Это непосредственно сервер который производит выдачу разрешений на доступ к ресурсам(токены, "секреты"). Если взять пример из реальной жизни, то oauth.vkontakte.ru. Сервер расположен по такому домену(поддомен) сервера социальной сети Вконтакте. Сервер ресурсов. Это собственно сервер на котором хранятся данные для доступа к которым все это (Oauth авторизация) была придумана. Это могут быть различные данные, например фотки, музыка, записи из социальной сети. Приложение, может не всегда быть всем общепринятым приложением (программой) это может быть, а обычно так часто и есть, сервер. С помощью этого приложения пользователь хочет без пароля получить какие-то данные которыми он владеет. Например возьмем пример вконтакте или твитер. Чтобы использовать их API нужно зарегистрировать приложение у них на сайте ( в разделе разработчик), это подразумевает получения каких-то индентификаторов/ключей ... строк текста, с помощью которых можно запрашивать у пользователя разрешение на доступ к данным. Например, чтобы делать авторизацию на сайте через Вконтакте, нужно зарегистрировать приложение, получить ключи/токен, и в тот момент когда пользователь хочет выполнить вход через Вконтакте пересылать этот ключ на сервер. Или же приложение для мобильного устройства через которое пользователь будет производить вход и доступ к данным.
Теперь, как происходит авторизация, в моем понимании.
Допустим пользователь Х хочет зайти на сайт S через социальную сеть вконтакте. Разработчик сайта заранее предусмотрел возможность авторизации через социальные сети и зарегистрировал свой сайт как приложение вконтакте, получил ключик/токен. Пользователь, клацает на красивую кнопочку "Вход через Вконтакте". Что в этот момент происходит. Сайт(сервер) берет ранее полученный ключик, прикрепляет его к запрос и идет по ссылке, oauth.vk.com. (Передавая свой ключ в GET параметрах или POST не столь важно). Сервер oauth.vk.com смотрит, так такое приложение есть в базе, значит можно доверять. Теперь интересный момент, если пользователь был ранее авторизирован Вконтакте,то ему показывают всплывающее окошко, "Вы разрешаете приложению.....". А вот если пользователь ранее не производил вход Вконтакте, то ему сначала предлагают ввести свой пароль и логин, а уже затем спросят разрешает ли он приложению...... После этого если пользователь нажал "Да", то сервер oauth генерирует токен доступа, который является своеобразным временным паролем. Записывает в базу сам токен и например дату истечения. Возвращает этот токен серверу (сайт с которого пользователь хотел войти ВК) по адресу указанному в Callback, сервер смотрит, забирает токен записывает себе в базу данных рядом с именем пользователя. Если же нет, получает отказ в каком-то виде. Получив токен "приложение" имеет доступ к данным пользователя ( если нету ограничений на стороне сервера ресурсов). По сути если написать таким образом вредоносную программу, то можно после получения токен слить или удалить всю информацию пользователя. Имея данный токен. Мы можем использовать его в нескольких целях : использую как подтверждение того, что пользователь прошел авторизацию и теперь давать ему доступ к данным на нашем сайте (сайт с которого пользователь нажимал кнопку "Войти вконтакте") постоянно проверяя токен при доступе к приватным ресурсам на нашем сайте. То есть тот же пароль или сессия на стороне сервера (кукисы ...). Или же с помощью нашего сайта делать что-то на странице пользователя вконтакте, загружать фото, делать посты......
Теперь вопросы которые возникли у меня, кроме главного (правильно ли я вообще понимаю).
Если перехватить токен, то можно использовать его как тот же пароль. В чем тогда преимущество? Можно ограничить ресурсы разными токенами? Он временный и через некоторое время уже будет недействительным, но если злоумышленник успеет сделать что-то раньше ? То есть передавать токен как и пароль нужно в пост запросе по HTTPS протоколу, чтобы защищать его как тот же пароль? Если я использую токен от Вконтакте как авторизацию на своем сайте, то каким образом поучать новый токен по истечению срока действия старого, заново запрашивать " Вы разрешаете приложению .... " через oauth сервер. Такого не встречал на сайтах, обычно один раз разрешил и все. Данные способ авторизации удобен когда у пользователя есть какой-то глобальный аккаунт на доверенном сервере.Если же нужна авторизация на своем сервере только и без сторонних oauth серверов, следует использовать похожий принцип просто, при первой авторизации один раз передавать логин и пароль по HTTPS и получать токен уже непосредственно у сервера. Допустим интернет магазин и клиент Android.Первый раз логин и пароль, в ответ прилетает токен и доступ к API производить уже передавая этот токен. Регистрация приложения является дополнительным уровнем безопасности, то есть чтобы не все могли попросить пользователя разрешить им доступ и получить токен. Если приложение допускает авторизацию, как прямо на самом сервер (интернет магазин) так и через OAuth (вконтакте), то кто должен контроллировать время жизни токена во втором случае. В первом случае мы работаем непосредственно с нашими сервером(интернет магазин), а во втором случае мы передаем и используем токен полученный от внешнего сервера OAuth (vkontakte) каким образом нам нужно обновлять токен, просить об этом у oauth.vk.com или нам в данном Вконтакте больше не нужен, после выдачи первого токена и теперь сами на стороне сервера интернет магазина генеируем новые токены ?
Всем спасибо, кто дочитал это.
Скажите, правильно ли я вообще понимаю принцип работы? Если нет, то скажите пожалуйста где и что не так.
Спасибо всем огромное


Ответ

В принципе правильно, хоть и запутанно немного описываете. Там всё проще )
По остальным вопросам:
Нет, не так-же. Разница в правах.
Для авторизации обычно не требуют прав больших чем "возможность получить имя и фамилию пользователя". В диалоге аутентификации приложения пользователь видит все права, которые оно у него требует, и если не согласен, то может отказаться.
Ну и на уровне API обычно не предусматривают никаких критичных действий. Скажем, пароль или e-mail пользователя по access-токену практически нигде нельзя поменять. Да, очень желательно. Ну и большинство сервисов вообще не позволяют работать через незашифрованное соединение.
Впрочем, в случае explict-авторизации, кроме как между сервисом и вашим сервером access-токен никуда не передается. Т.е. при использовании незашифрованного соединения, перехватить токен может может только ваш хостер, либо провайдер вашего хостера. Что впрочем, тоже потенциально может быть неприятным. Делают обычно по другому - через запрос нового access-токена, используя статичный refresh-токен и старый access-токен.
Пользователь при этом ничего не видит, но access-токены постоянно заменяются на новые. У кого-то это происходит раз в день, а у кого-то раз в пару часов. Скомпрометированные токены при этом естественно становятся недействительными.
В случае получения запроса с некорректной комбинацией refresh-токена и access-токена, сервис автоматически должен сделать оба токена недействительными, ну и соответственно в этом случае пользователь увидит окошко аутентификации приложения заново.
Но это если сервис вообще выдает refresh-токены. К сожалению они опциональны, и ВКонтакте не выдает их, в отличии от Google и Facebook. В этом случае можно только периодически запрашивать что-то доступное вашему приложению. После истечения срока токена, сервис будет выдавать в ответ ошибку 400 с error=invalid_grant - в этом случае приложение должно снова перекинуть пользователя на форму аутентификации приложения на сервисе, дабы получить новый access-токен. Да, так. Так многие и делают.
По похожему принципу, кстати, работает авторизация на большинстве сайтов ещё со времен создания интернета. Логин-пароль передаются сервису только один раз, возвращается некий токен(например хеш чего-либо), он сохраняется в некое хранилище(например в cookies), ну и собственно этот токен используется используется для авторизации в последующие разы. Пресловутые же email и пароль по такому токену менять не позволяют. Не только. Основная цель регистрации приложений - дать возможность администрации отключить всё приложение разработчика в связи с выполнением приложением каких-то вредоносных действий, или в случае массовой компрометации access-токенов пользователей этого приложения. Нет, не на самом сервере приложения. Сервер приложения просто отправляет сервису запрос на refresh, и в ответ получает новый access-token.