/// <summary> /// Rigenera l'access token di OAuth2 partendo da un refresh token. /// </summary> /// <param name="owinContext">Il contesto HTTP di OWIN.</param> /// <param name="clientId">Il client ID a cui sono legati i token.</param> /// <param name="currentTokens">I token attualmente disponibili.</param> /// <returns>I nuovi token ottenuti in seguito al refresh.</returns> public async Task<OAuth2TokensCookie> RefreshAsync(IOwinContext owinContext, string clientId, OAuth2TokensCookie currentTokens) { // Registro la data di inizio in maniera conservativa. var accessTokenCreatedOn = _clock.UtcNow; var address = AuthorizationSettings.AccessTokenRefreshEndpointUri.AbsoluteUri; var clientSecret = AuthorizationSettings.Clients[clientId].ClientSecret; // Attivo il client per il refresh dei token. var tokenClient = new TokenClient(address, clientId, clientSecret); var tokenResponse = await tokenClient.RequestRefreshTokenAsync(currentTokens.RefreshToken); // Registro il più possibile in caso di errore. if (tokenResponse.IsError || tokenResponse.IsHttpError) { var tokenRespErr = string.IsNullOrWhiteSpace(tokenResponse.Error) ? tokenResponse.HttpErrorReason : tokenResponse.Error; var errMsg = $"{tokenResponse.HttpErrorStatusCode} - {tokenRespErr}"; _log.Error(new LogMessage { ShortMessage = errMsg, LongMessage = tokenResponse.Raw, Context = "Requesting new access and refresh tokens", Arguments = new[] { KeyValuePair.Create("token_status_code", tokenResponse.HttpErrorStatusCode.ToString()), KeyValuePair.Create("token_type", tokenResponse.TokenType) } }); // Lancio un'eccezione per uscire immediatamente. throw new Exception(errMsg); } return new OAuth2TokensCookie { AccessToken = tokenResponse.AccessToken, AccessTokenCreatedOn = accessTokenCreatedOn, AccessTokenExpiresIn = tokenResponse.ExpiresIn, RefreshToken = tokenResponse.RefreshToken, RandomToken = currentTokens.RandomToken }; }
/// <summary> /// Richiede una batteria di token partendo da un dato authorization code. /// </summary> /// <param name="owinContext">Il contesto HTTP di OWIN.</param> /// <param name="clientId">Il client ID a cui sono legati i token.</param> /// <param name="authorizationCode">L'authorization code.</param> /// <returns>La batteria di token recuperati partendo dall'authorization code.</returns> public async Task<OAuth2TokensGenerationResult> RequestTokensAsync(IOwinContext owinContext, string clientId, string authorizationCode) { // Registro la data di inizio in maniera conservativa. var accessTokenCreatedOn = _clock.UtcNow; var address = AuthorizationSettings.AccessTokenRequestEndpointUri.AbsoluteUri; var clientSecret = AuthorizationSettings.Clients[clientId].ClientSecret; // Attivo il client per il recupero dei token. var tokenClient = new TokenClient(address, clientId, clientSecret); var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(authorizationCode, owinContext.Request.Uri.AbsoluteUri); if (tokenResponse.IsError) { Log.Error(tokenResponse.Error); throw new Exception(tokenResponse.Error); } // Copio tutti i token ricevuti in un cookie. var oauth2TokensCookie = new OAuth2TokensCookie { AccessToken = tokenResponse.AccessToken, AccessTokenCreatedOn = accessTokenCreatedOn, AccessTokenExpiresIn = tokenResponse.ExpiresIn, RefreshToken = tokenResponse.RefreshToken, RandomToken = Guid.NewGuid() }; // Recupero l'identity token, se presente. var identityToken = string.IsNullOrWhiteSpace(tokenResponse.IdentityToken) ? Option.None<string>() : Option.Some(tokenResponse.IdentityToken); return new OAuth2TokensGenerationResult { TokensCookie = oauth2TokensCookie, IdentityToken = identityToken }; }
private async Task<OAuth2TokensCookie> RefreshAccessTokenAsync(IOwinContext owinContext, string clientId, OAuth2TokensCookie oauth2TokensCookie, string oauth2CsrfCookieContent) { // Indica se il token possa essere rinfrescato, o se vi siano altri tentativi concorrenti. var canRefresh = false; try { lock (_refreshingTokens) { if (!_refreshingTokens.Contains(oauth2TokensCookie.RandomToken)) { canRefresh = true; _refreshingTokens.Add(oauth2TokensCookie.RandomToken); } } if (!canRefresh) { Log.Warn("Another thread or request is refreshing the same token, going to skip refresh at all"); return oauth2TokensCookie; } // Effettuo la richiesta per ottenere una nuova serie di token. oauth2TokensCookie = await _accessTokenRefresher.RefreshAsync(owinContext, clientId, oauth2TokensCookie); // Nel caso sia andato tutto bene, aggiorno il cookie con i token. var oauth2TokensCookieContent = await _tokensCookieEncoder.EncodeAsync(oauth2TokensCookie); // Elaboro le date di fine dei token e dei cookie. var accessTokenExpiresOn = oauth2TokensCookie.AccessTokenCreatedOn.AddSeconds(oauth2TokensCookie.AccessTokenExpiresIn); // Aggiungo il cookie con i token alla response. owinContext.Response.Cookies.Append(OAuth2TokensCookie.CookieName, oauth2TokensCookieContent, new CookieOptions { HttpOnly = true, // Fondamentale che NON sia accessibile da JavaScript. Expires = accessTokenExpiresOn }); // Aggiungo il cookie di verifica per evitare XSRF. owinContext.Response.Cookies.Append(OAuth2CsrfCookie.CookieName, oauth2CsrfCookieContent, new CookieOptions { HttpOnly = false, // Fondamentale che SIA accessibile anche da JavaScript. Expires = accessTokenExpiresOn }); return oauth2TokensCookie; } catch (Exception ex) { Log.Warn("An error occurred while trying to refresh the access token", ex); return oauth2TokensCookie; } finally { if (canRefresh) { lock (_refreshingTokens) { _refreshingTokens.Remove(oauth2TokensCookie.RandomToken); } } } }