using System; using System.Reactive.Linq; using Octokit.Reactive; namespace Octokit { public static class AuthorizationExtensions { /// /// This method will create a new authorization for the specified OAuth application, only if an authorization /// for that application doesn’t already exist for the user. It returns the user’s token for the application /// if one exists. Otherwise, it creates a new one. /// /// /// /// This method allows the caller to provide a callback which is used to retrieve the two-factor code from /// the user. Typically the callback is used to show some user interface to the user. /// /// /// See API documentation /// for more details. /// /// /// The this method extends /// Client ID for the OAuth application that is requesting the token /// The client secret /// Defines the scopes and metadata for the token /// Callback used to retrieve the two-factor authentication code /// from the user /// public static IObservable GetOrCreateApplicationAuthentication( this IObservableAuthorizationsClient authorizationsClient, string clientId, string clientSecret, NewAuthorization newAuthorization, Func> twoFactorChallengeHandler) { Ensure.ArgumentNotNull(authorizationsClient, "authorizationsClient"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNull(newAuthorization, "newAuthorization"); return authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization) .Catch(exception => twoFactorChallengeHandler(exception) .SelectMany(result => result.ResendCodeRequested ? authorizationsClient.GetOrCreateApplicationAuthentication( clientId, clientSecret, newAuthorization, twoFactorChallengeHandler) : authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization, result.AuthenticationCode))); } /// /// This method will create a new authorization for the specified OAuth application. If an authorization /// for that application already exists for the user and fingerprint, it'll delete the existing one and /// recreate it. /// /// /// /// This method is typically used to initiate an application authentication flow. /// This method allows the caller to provide a callback which is used to retrieve the two-factor code from /// the user. Typically the callback is used to show some user interface to the user. /// /// /// See API documentation /// for more details. /// /// /// The this method extends /// Client ID for the OAuth application that is requesting the token /// The client secret /// Defines the scopes and metadata for the token /// Callback used to retrieve the two-factor authentication code /// from the user /// If true, instead of completing when the two factor code supplied /// is invalid, we go through the whole cycle again and prompt the two factor dialog. /// public static IObservable CreateAndDeleteExistingApplicationAuthorization( this IObservableAuthorizationsClient authorizationsClient, string clientId, string clientSecret, NewAuthorization newAuthorization, Func> twoFactorChallengeHandler, bool retryInvalidTwoFactorCode) { return authorizationsClient.CreateAndDeleteExistingApplicationAuthorization( clientId, clientSecret, newAuthorization, twoFactorChallengeHandler, null, retryInvalidTwoFactorCode); } public static IObservable CreateAndDeleteExistingApplicationAuthorization( this IObservableAuthorizationsClient authorizationsClient, string clientId, string clientSecret, NewAuthorization newAuthorization, Func> twoFactorChallengeHandler, string twoFactorAuthenticationCode, bool retryInvalidTwoFactorCode) { Ensure.ArgumentNotNull(authorizationsClient, "authorizationsClient"); Ensure.ArgumentNotNullOrEmptyString(clientId, "clientId"); Ensure.ArgumentNotNullOrEmptyString(clientSecret, "clientSecret"); Ensure.ArgumentNotNull(newAuthorization, "newAuthorization"); // If retryInvalidTwoFactorCode is false, then we only show the TwoFactorDialog when we catch // a TwoFactorRequiredException. If it's true, we show it for TwoFactorRequiredException and // TwoFactorChallengeFailedException Func> twoFactorHandler = ex => retryInvalidTwoFactorCode || ex is TwoFactorRequiredException ? twoFactorChallengeHandler(ex) : Observable.Throw(ex); return authorizationsClient.CreateAuthorizationAndDeleteExisting( clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode) .Catch( exception => twoFactorHandler(exception) .SelectMany(result => result.ResendCodeRequested ? authorizationsClient.CreateAndDeleteExistingApplicationAuthorization( clientId, clientSecret, newAuthorization, twoFactorHandler, null, // twoFactorAuthenticationCode retryInvalidTwoFactorCode) : authorizationsClient.CreateAndDeleteExistingApplicationAuthorization( clientId, clientSecret, newAuthorization, twoFactorHandler, result.AuthenticationCode, retryInvalidTwoFactorCode))); } // If the Application Authorization already exists, the result might have an empty string as the token. This is // because GitHub.com no longer stores the token, but stores a hashed version of it. It is assumed that clients // will store the token locally. // The only reason to be calling GetOrCreateApplicationAuthentication is pretty much the case when you are // logging in and thus don't have the token already. So if the token returned is an empty string, we'll go // ahead and delete it for you and then recreate it. static IObservable CreateAuthorizationAndDeleteExisting( this IObservableAuthorizationsClient authorizationsClient, string clientId, string clientSecret, NewAuthorization newAuthorization, string twoFactorAuthenticationCode = null) { return authorizationsClient.GetOrCreateAuthorizationUnified( clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode) .SelectMany(authorization => string.IsNullOrEmpty(authorization.Token) ? authorizationsClient.Delete(authorization.Id, twoFactorAuthenticationCode) .SelectMany(_ => authorizationsClient.CreateNewAuthorization( clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode)) : Observable.Return(authorization)); } static IObservable GetOrCreateAuthorizationUnified( this IObservableAuthorizationsClient authorizationsClient, string clientId, string clientSecret, NewAuthorization newAuthorization, string twoFactorAuthenticationCode = null) { return string.IsNullOrEmpty(twoFactorAuthenticationCode) ? authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization) : authorizationsClient.GetOrCreateApplicationAuthentication(clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode); } static IObservable CreateNewAuthorization( this IObservableAuthorizationsClient authorizationsClient, string clientId, string clientSecret, NewAuthorization newAuthorization, string twoFactorAuthenticationCode = null) { return string.IsNullOrEmpty(twoFactorAuthenticationCode) ? authorizationsClient.Create(clientId, clientSecret, newAuthorization) : authorizationsClient.Create(clientId, clientSecret, newAuthorization, twoFactorAuthenticationCode); } } }