/// <summary> /// Unisce i due URI dati. /// </summary> /// <param name="baseUri">Il primo URI.</param> /// <param name="relativePath">Il secondo URI.</param> /// <param name="queryStringBuilder">La componente della query string.</param> /// <returns>I due URI uniti.</returns> private static string UriCombine(string baseUri, PathString relativePath, QueryStringBuilder queryStringBuilder = null) { var trimmedBaseUri = baseUri.TrimEnd(UrlTrimChars); var trimmedRelativePathValue = relativePath.HasValue ? relativePath.Value.TrimStart(UrlTrimChars) : string.Empty; return string.Format("{0}/{1}{2}", trimmedBaseUri, trimmedRelativePathValue, queryStringBuilder?.ToString() ?? string.Empty); }
/// <summary> /// Gestisce l'handler di tipo REDIRECT per LOGIN, che si occupa di generare i token dopo /// la login. /// </summary> /// <param name="owinContext">Il contesto di OWIN.</param> /// <returns>Un task.</returns> private async Task HandleLoginRedirectAsync(IOwinContext owinContext) { // Recupero le informazioni su request e response. var owinRequest = owinContext.Request; var owinResponse = owinContext.Response; string authCode; switch (owinRequest.Method) { case "GET": case "get": Raise.InvalidOperationException.If(_settings.ResponseMode != OAuth2ResponseMode.FragmentEncodedRedirect); // Visto che il flusso utilizzato è di tipo "code grant", mi aspetto che nel // query string sia presenta il codice "code" per proseguire il flusso. authCode = owinRequest.Query["code"]; break; case "POST": case "post": Raise.InvalidOperationException.If(_settings.ResponseMode != OAuth2ResponseMode.FormPost); // Visto che il flusso utilizzato è di tipo "code grant", mi aspetto che il query // string mi sia stato inviato in POST. using (var sr = new StreamReader(owinRequest.Body)) { var qs = new QueryStringBuilder(await sr.ReadToEndAsync()); authCode = qs["code"]; } break; default: throw new NotSupportedException($"HTTP method {owinRequest.Method} is not supported for redirect"); } // Recupero i token tramite l'authorization code appena ricevuto. var oauth2TokensGenRes = await _authCodeHandler.RequestTokensAsync(owinContext, _settings.ClientId, authCode); var userValidationResult = await _userValidator.ValidateAsync(owinContext, _settings.ClientId, oauth2TokensGenRes); if (!userValidationResult.Authorized) { // Effetto il redirect verso la pagina di errore ed esco dal componente di middleware. await HandleErrorRedirectAsync(owinContext, userValidationResult.AuthorizationDeniedException, userValidationResult.AuthorizationDeniedReason); return; } // Preparo il cookie con i token. var oauth2TokensCookie = oauth2TokensGenRes.TokensCookie; var oauth2TokensCookieContent = await _tokensCookieEncoder.EncodeAsync(oauth2TokensCookie); // Preparo il cookie di verifica. var oauth2CsrfCookie = new OAuth2CsrfCookie { RandomToken = oauth2TokensCookie.RandomToken }; var oauth2CsrfCookieContent = await _csrfCookieEncoder.EncodeAsync(oauth2CsrfCookie); // 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. owinResponse.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. owinResponse.Cookies.Append(OAuth2CsrfCookie.CookieName, oauth2CsrfCookieContent, new CookieOptions { HttpOnly = false, // Fondamentale che SIA accessibile anche da JavaScript. Expires = accessTokenExpiresOn }); // Aggiungo il cookie con l'identity token, se presente. if (oauth2TokensGenRes.IdentityToken.HasValue) { // Non imposto data di scadenza, in quanto è un cookie di sessione. owinResponse.Cookies.Append(IdentityTokenCookieName, oauth2TokensGenRes.IdentityToken.Value, new CookieOptions { HttpOnly = true, // Non deve essere visto da JavaScript. }); } // Effettuo il redirect verso la pagina specificata ed esco dal componente di middleware. owinResponse.Redirect(_settings.RedirectUri.AbsoluteUri); }
/// <summary> /// Gestisce l'handler di tipo LOGOUT, che redirige tutte le chiamate verso il portale di OAuth2. /// </summary> /// <param name="owinContext">Il contesto di OWIN.</param> /// <returns>Un task.</returns> private Task HandleLogoutAsync(IOwinContext owinContext) { // Recupero le informazioni su request e response. var owinRequest = owinContext.Request; var owinResponse = owinContext.Response; var identityTokenCookieContent = owinRequest.Cookies[IdentityTokenCookieName]; if (_settings.PerformEndSessionRedirect && !string.IsNullOrWhiteSpace(identityTokenCookieContent)) { // Normalizzo l'indirizzo della request per poi effettuare la trasformazione da // "/logout" a "/logoutRedirect", al fine di avere l'indirizzo giusto per il // parametro "post_logout_redirect_uri". var lowerRequestUri = owinRequest.Uri.AbsoluteUri.ToLowerInvariant().TrimEnd(UrlTrimChars); var postLogoutRedirectUri = lowerRequestUri.Replace(LogoutPath, LogoutRedirectPath); // Preparo il query string richiesto da "endsession". var qsBuilder = new QueryStringBuilder() .Add("id_token_hint", identityTokenCookieContent) .Add("post_logout_redirect_uri", postLogoutRedirectUri); // Effettuo il redirect ed esco dal componente di middleware. var url = UriCombine(_settings.OAuth2EndpointUri.AbsoluteUri, _settings.EndSessionPath, qsBuilder); _log.Debug($"Performing redirect to {url}"); owinResponse.Redirect(url); return TaskHelper.CompletedTask; } // Effettuo il redirect verso la pagina indicata ed esco dal componente di middleware. return HandleLogoutRedirectAsync(owinContext); }
/// <summary> /// Gestisce l'handler di tipo LOGIN, che redirige tutte le chiamate verso il portale di OAuth2. /// </summary> /// <param name="owinRequest">La richiesta OWIN.</param> /// <param name="owinResponse">La risposta OWIN.</param> /// <returns>Un task.</returns> private Task HandleLoginAsync(IOwinRequest owinRequest, IOwinResponse owinResponse) { // Faccio la decodifica del tipo del flusso. string responseType; switch (_settings.ResponseType) { case OAuth2ResponseType.AuthorizationCode: responseType = "code"; break; case OAuth2ResponseType.IdentityTokenAndAccessToken: responseType = "id_token token"; break; case OAuth2ResponseType.Hybrid: responseType = "code id_token token"; break; default: responseType = "token"; break; } // Normalizzo l'indirizzo della request per poi effettuare la trasformazione da "/login" // a "/loginRedirect", al fine di avere l'indirizzo giusto per il parametro "redirect_uri". var lowerRequestUri = owinRequest.Uri.AbsoluteUri.ToLowerInvariant().TrimEnd(UrlTrimChars); var redirectUri = lowerRequestUri.Replace(LoginPath, LoginRedirectPath); // Costruisce l'indirizzo a cui fare redirect. var qsBuilder = new QueryStringBuilder() .Add("client_id", _settings.ClientId) .Add("redirect_uri", redirectUri) .Add("response_type", responseType); if (_settings.Scopes != null && _settings.Scopes.Count > 0) { qsBuilder.Add("scope", string.Join(" ", _settings.Scopes)); } if (_settings.NonceGenerationMode == OAuth2NonceGenerationMode.Auto) { qsBuilder.Add("nonce", UniqueIdGenerator.NewBase32("-")); } if (_settings.ResponseMode == OAuth2ResponseMode.FormPost) { qsBuilder.Add("response_mode", "form_post"); } // Effettuo il redirect ed esco dal componente di middleware. var url = UriCombine(_settings.OAuth2EndpointUri.AbsoluteUri, _settings.AuthorizePath, qsBuilder); _log.Debug($"Performing redirect to {url}"); owinResponse.Redirect(url); return TaskHelper.CompletedTask; }