Я столкнулся с очень странной проблемой, когда токен refre sh "исчезает" через несколько часов в службе приложений Azure, в которой размещается мой проект Wep Api. Я реализовал OAuth для своего потока паролей. Срок действия нашего AccessToken истекает через 1 час, а нашего RefreshToken истекает через одну неделю.
Для некоторого фона. Это происходит в службе приложений Azure, где я размещаю свой веб-интерфейс, и мобильный интерфейс выполняет вызовы к нему (есть несколько пользователей / мобильных устройств, которые звонят в эту службу приложения).
Вот как выглядит пример исходного вызова с использованием вызова /token
:
![enter image description here](https://i.stack.imgur.com/siPuj.png)
Мой grant_type
- это пароль. Обычно я получаю обратно поле refresh_token
вместе с access_token
, token_type
и expires_in
.
Работает нормально в течение первых нескольких часов после того, как я пу sh в службу приложений, затем refresh_token исчезает , Я действительно озадачен этой проблемой.
Вот мой CreateAsync
Код:
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
string refreshTokenId = await CreateRefreshTokenId(clientid, context);
if (refreshTokenId != null)
{
context.SetToken(refreshTokenId);
}
else
{
throw new Exception("refresh token could not be created");
}
}
private async Task<string> CreateRefreshTokenId(string clientId, AuthenticationTokenCreateContext context)
{
var ticket = context.Ticket;
var refreshTokenId = Guid.NewGuid().ToString("n");
var refreshTokenLifeTime = ConfigurationManager.AppSettings["as:clientRefreshTokenLifeTime"];
var token = new CreateRefreshTokenDTO
{
RefreshTokenId = refreshTokenId,
ClientId = clientId,
Subject = ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
ticket.Properties.IssuedUtc = token.IssuedUtc;
ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await createRefreshTokenManager.ManagerRequest(new CreateRefreshTokenRequest
{
RefreshToken = token
});
return result.IsError ? null : refreshTokenId;
}
Я добавил исключение в оператор else, чтобы посмотреть, будет ли оно выбрасывать и исключение, и оно на самом деле бросает, что приводит меня к мысли, что refreshTokenId является нулевым. Я также добавил ведение журнала в таблицу журналов, но по какой-то причине, когда выдается эта ошибка, она должна быть сохранена в таблице БД (которую я тестировал локально и работает), но на сервере приложений она не сохраняется в таблице. Очень сбивает с толку ... ОБНОВЛЕНИЕ: ПОЖАЛУЙСТА, СМОТРИТЕ ОБНОВЛЕНИЕ НИЖЕ, ПОЧЕМУ БЕЗ ЛОГОВ НЕ СОХРАНИЛСЯ
Затем, то, что должно произойти после этого, это то, что теперь передний конец (мобильный, в этом case) имеет токены доступа и refre sh; по истечении срока действия токена делается еще один вызов /token
, но с grant_type = refresh_token
: ![enter image description here](https://i.stack.imgur.com/6zxyA.png)
ОБНОВЛЕНИЕ
В конце концов я смог воспроизвести проблему локально методом проб и ошибок и дождался окончания срока действия маркера доступа (не совсем уверен). Но в любом случае я смог выдать эту ошибку:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
Эта ошибка была причиной, по которой я не смог сохранить какие-либо журналы в БД.
Я использую Castle Windsor как мой Io C и EF6. Мои звонки в следующем порядке:
1] Попытка проверить контекст. Здесь я делаю еще один await
вызов LoginUserManager
, где я в основном получаю и проверяю информацию пользователя
// This is used for validating the context
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
2] CreateAsyn c для создания доступа и обновления sh токенов из Context
public async Task CreateAsync(AuthenticationTokenCreateContext context)
Внутри CreateAsyn c Я делаю await
вызов CreateOrUpdateRefreshTokenManager
, который либо выполняет обновление, если запись существует, либо создание. И в конечном итоге сделать SaveChanges()
. Это SaveChanges()
и является причиной ошибки Если я не вызову SaveChanges()
, в этой таблице не обновляется и не создается запись. Это странно, потому что в других частях моего кода я вообще не вызываю SaveChanges()
в конце жизненного цикла веб-запроса, но производится обновление / создание / удаление. Я предполагаю, что EF / Windsor обрабатывает сохранение для меня.
Я думаю, что, поскольку этот поток отличается от всех других моих конечных точек, и что его обработка двух Asyn c вызывает, что где-то посередине я избавляюсь от DbContext и, возможно, именно поэтому я вижу его сбой при втором (CreateAsync
) вызове. Не уверен, просто моя мысль здесь.
Во всяком случае, извините за длинную почту здесь. Я хотел опубликовать как можно больше информации и надеюсь, что это также может помочь кому-то, кто сталкивается с этой или подобной проблемой.
Спасибо!
ОБНОВЛЕНИЕ 2
Я заметил, что после получения этой ошибки при вызове /token
, любые другие (AllowAnonymous) вызовы, которые я выполняю, даже те, которые связаны с БД. Но, в частности, вызов /token
больше не работает. Единственный способ обойти это - перезапустить сервер.
ОБНОВЛЕНИЕ 3 Мне удалось воспроизвести эту проблему ТОЛЬКО на мобильном тестировании (связанном с Azure сервером), но я не могу воспроизвести локально. Шаги, которые я использовал для воспроизведения:
- Вход с одной учетной записью
- Выход
- Вход с другой учетной записью
- Выход
- Войдите в систему с первой учетной записью, которую я пытался) - This FAILS