Beispiel #1
0
        public static OAuthTokenCredential GetAuthenticationInformationForDatabaseUrl(string databaseUrl, string refreshToken = null, Func <OAuthRequest, OAuthResponse> requestCallback = null)
        {
            var instanceUrl = GetInstanceUrl(databaseUrl);

            // deserialize token cache from file (only if file exists and has changed since last deserialization)
            DeserializeTokenCache();

            var result = TryGetCurrentOAuthToken(instanceUrl, ref refreshToken);

            if (result != null)
            {
                return(result);
            }

            var authority = CreateAuthorityAsync(instanceUrl)
                            .ConfigureAwait(false)
                            .GetAwaiter()
                            .GetResult();

            var tokenClient = CreateTokenClient(authority);

            result = TryGetOAuthTokenFromRefreshTokenAsync(tokenClient, authority, refreshToken)
                     .ConfigureAwait(false)
                     .GetAwaiter()
                     .GetResult();

            if (result != null)
            {
                StoreToken(instanceUrl, result);
                return(result);
            }

            // no refresh token or refresh token expired, do the full auth cycle eventually involving a password prompt
            if (requestCallback != null)
            {
                var cryptoNumbers = new CryptoNumbers();
                var startUrl      = CreateOAuthStartUrl(authority, cryptoNumbers);

                var request  = new OAuthRequest(startUrl, RedirectUri);
                var response = requestCallback(request)?.ToAuthorizeResponse();
                if (response != null)
                {
                    result = TryGetOAuthTokenFromAuthorizeResponseAsync(tokenClient, cryptoNumbers, response)
                             .ConfigureAwait(false)
                             .GetAwaiter()
                             .GetResult();
                    if (result != null)
                    {
                        StoreToken(instanceUrl, result);
                        return(result);
                    }
                }
            }

            return(null);
        }
Beispiel #2
0
        private static string CreateOAuthStartUrl(string authority, CryptoNumbers cryptoNumbers)
        {
            // FUTURE: discover authorize endpoint via ".well-known/openid-configuration"
            var request = new AuthorizeRequest(authority + "/connect/authorize");

            return(request.CreateAuthorizeUrl(
                       clientId: ClientId,
                       responseType: "id_token code",
                       responseMode: "form_post",
                       scope: "openid profile email offline_access piweb",
                       redirectUri: RedirectUri,
                       state: cryptoNumbers.State,
                       nonce: cryptoNumbers.Nonce,
                       codeChallenge: cryptoNumbers.Challenge,
                       codeChallengeMethod: OidcConstants.CodeChallengeMethods.Sha256));
        }
Beispiel #3
0
        public static async Task <OAuthTokenCredential> GetAuthenticationInformationForDatabaseUrlAsync(string databaseUrl, string refreshToken = null, Func <OAuthRequest, Task <OAuthResponse> > requestCallbackAsync = null)
        {
            var instanceUrl = GetInstanceUrl(databaseUrl);

            var result = TryGetCurrentOAuthToken(instanceUrl, ref refreshToken);

            if (result != null)
            {
                return(result);
            }

            var authority = await CreateAuthorityAsync(instanceUrl).ConfigureAwait(false);

            var tokenClient = CreateTokenClient(authority);

            result = await TryGetOAuthTokenFromRefreshTokenAsync(tokenClient, authority, refreshToken).ConfigureAwait(false);

            if (result != null)
            {
                AccessTokenCache.Store(instanceUrl, result);
                return(result);
            }

            // no refresh token or refresh token expired, do the full auth cycle eventually involving a password prompt
            if (requestCallbackAsync != null)
            {
                var cryptoNumbers = new CryptoNumbers();
                var startUrl      = CreateOAuthStartUrl(authority, cryptoNumbers);

                var request  = new OAuthRequest(startUrl, RedirectUri);
                var response = (await requestCallbackAsync(request).ConfigureAwait(false))?.ToAuthorizeResponse();
                if (response != null)
                {
                    result = await TryGetOAuthTokenFromAuthorizeResponseAsync(tokenClient, cryptoNumbers, response).ConfigureAwait(false);

                    if (result != null)
                    {
                        AccessTokenCache.Store(instanceUrl, result);
                        return(result);
                    }
                }
            }

            return(null);
        }
Beispiel #4
0
        private static async Task <OAuthTokenCredential> TryGetOAuthTokenFromAuthorizeResponseAsync(TokenClient tokenClient, CryptoNumbers cryptoNumbers, AuthorizeResponse response)
        {
            if (response != null)
            {
                // claims des IdentityToken decodieren
                var claims = DecodeSecurityToken(response.IdentityToken).Claims.ToArray();

                // die folgenden validierungen sind notwendig, um diversen CSRF / man in the middle / etc. Angriffsszenarien zu begegnen
                // state validieren
                if (!string.Equals(cryptoNumbers.State, response.State, StringComparison.Ordinal))
                {
                    throw new InvalidOperationException("invalid state value in openid service responce.");
                }

                // nonce validieren
                if (!ValidateNonce(cryptoNumbers.Nonce, claims))
                {
                    throw new InvalidOperationException("invalid nonce value in identity token.");
                }

                // c_hash validieren
                if (!ValidateCodeHash(response.Code, claims))
                {
                    throw new InvalidOperationException("invalid c_hash value in identity token.");
                }

                // code eintauschen gegen access token und refresh token, dabei den code verifier mitschicken, um man-in-the-middle Angriff auf authorization code zu begegnen (PKCE)
                var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
                    code : response.Code,
                    redirectUri : RedirectUri,
                    codeVerifier : cryptoNumbers.Verifier).ConfigureAwait(false);

                if (tokenResponse.IsError)
                {
                    throw new InvalidOperationException("error during request of access token using authorization code: " + tokenResponse.Error);
                }

                return(OAuthTokenCredential.CreateWithIdentityToken(tokenResponse.IdentityToken, tokenResponse.AccessToken, DateTime.UtcNow + TimeSpan.FromSeconds(tokenResponse.ExpiresIn), tokenResponse.RefreshToken));
            }

            return(null);
        }