mirror of
https://github.com/bitwarden/mobile
synced 2026-01-08 11:33:31 +00:00
Converted auth to identity server endpoints and utilize bearer2 access token
This commit is contained in:
@@ -210,12 +210,13 @@ namespace Bit.Android
|
||||
.RegisterType<ILocalizeService, LocalizeService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILogService, LogService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IHttpService, HttpService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ITokenService, TokenService>(new ContainerControlledLifetimeManager())
|
||||
// Repositories
|
||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILoginRepository, LoginRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILoginApiRepository, LoginApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IConnectApiRepository, ConnectApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ICipherApiRepository, CipherApiRepository>(new ContainerControlledLifetimeManager())
|
||||
|
||||
@@ -3,9 +3,8 @@ using Bit.App.Models.Api;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
public interface IAuthApiRepository
|
||||
public interface IConnectApiRepository
|
||||
{
|
||||
Task<ApiResult<TokenResponse>> PostTokenAsync(TokenRequest requestObj);
|
||||
Task<ApiResult<TokenResponse>> PostTokenTwoFactorAsync(TokenTwoFactorRequest requestObj);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models.Api;
|
||||
using System;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
public interface IAuthService
|
||||
{
|
||||
bool IsAuthenticated { get; }
|
||||
bool IsAuthenticatedTwoFactor { get; }
|
||||
string Token { get; set; }
|
||||
string UserId { get; set; }
|
||||
string PreviousUserId { get; }
|
||||
bool UserIdChanged { get; }
|
||||
@@ -16,6 +15,5 @@ namespace Bit.App.Abstractions
|
||||
|
||||
void LogOut();
|
||||
Task<ApiResult<TokenResponse>> TokenPostAsync(TokenRequest request);
|
||||
Task<ApiResult<TokenResponse>> TokenTwoFactorPostAsync(TokenTwoFactorRequest request);
|
||||
}
|
||||
}
|
||||
|
||||
19
src/App/Abstractions/Services/ITokenService.cs
Normal file
19
src/App/Abstractions/Services/ITokenService.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
public interface ITokenService
|
||||
{
|
||||
string Token { get; set; }
|
||||
string RefreshToken { get; set; }
|
||||
[Obsolete("Old auth scheme")]
|
||||
string AuthBearer { get; set; }
|
||||
DateTime TokenExpiration { get; }
|
||||
bool TokenExpired { get; }
|
||||
TimeSpan TokenTimeRemaining { get; }
|
||||
bool TokenNeedseRefresh { get; }
|
||||
string TokenUserId { get; }
|
||||
string TokenEmail { get; }
|
||||
string TokenName { get; }
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Abstractions\Repositories\IAccountsApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\IDeviceApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Services\ITokenService.cs" />
|
||||
<Compile Include="Abstractions\Services\IHttpService.cs" />
|
||||
<Compile Include="Abstractions\Services\IDeviceInfoService.cs" />
|
||||
<Compile Include="Abstractions\Services\IGoogleAnalyticsService.cs" />
|
||||
@@ -88,7 +89,6 @@
|
||||
<Compile Include="Models\Api\Request\LoginRequest.cs" />
|
||||
<Compile Include="Models\Api\Request\PasswordHintRequest.cs" />
|
||||
<Compile Include="Models\Api\Request\TokenRequest.cs" />
|
||||
<Compile Include="Models\Api\Request\TokenTwoFactorRequest.cs" />
|
||||
<Compile Include="Models\Api\Response\CipherHistoryResponse.cs" />
|
||||
<Compile Include="Models\Api\Response\CipherResponse.cs" />
|
||||
<Compile Include="Models\Api\Response\ErrorResponse.cs" />
|
||||
@@ -140,12 +140,12 @@
|
||||
<Compile Include="Abstractions\Repositories\ILoginRepository.cs" />
|
||||
<Compile Include="Repositories\ApiRepository.cs" />
|
||||
<Compile Include="Repositories\AccountsApiRepository.cs" />
|
||||
<Compile Include="Repositories\ConnectApiRepository.cs" />
|
||||
<Compile Include="Repositories\BaseApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\IApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\IFolderApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\ILoginApiRepository.cs" />
|
||||
<Compile Include="Repositories\AuthApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\IAuthApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\IConnectApiRepository.cs" />
|
||||
<Compile Include="Repositories\DeviceApiRepository.cs" />
|
||||
<Compile Include="Repositories\CipherApiRepository.cs" />
|
||||
<Compile Include="Abstractions\Repositories\ICipherApiRepository.cs" />
|
||||
@@ -180,6 +180,7 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>AppResources.zh-Hans.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Services\TokenService.cs" />
|
||||
<Compile Include="Services\AppIdService.cs" />
|
||||
<Compile Include="Abstractions\Services\ILockService.cs" />
|
||||
<Compile Include="Services\LockService.cs" />
|
||||
|
||||
@@ -1,9 +1,41 @@
|
||||
namespace Bit.App.Models.Api
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bit.App.Models.Api
|
||||
{
|
||||
public class TokenRequest
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string MasterPasswordHash { get; set; }
|
||||
public string Token { get; set; }
|
||||
public int? Provider { get; set; }
|
||||
public DeviceRequest Device { get; set; }
|
||||
|
||||
public IDictionary<string, string> ToIdentityTokenRequest()
|
||||
{
|
||||
var dict = new Dictionary<string, string>
|
||||
{
|
||||
{ "grant_type", "password" },
|
||||
{ "username", Email },
|
||||
{ "password", MasterPasswordHash },
|
||||
{ "scope", "api offline_access" },
|
||||
{ "client_id", "mobile" }
|
||||
};
|
||||
|
||||
if(Device != null)
|
||||
{
|
||||
dict.Add("DeviceType", Device.Type.ToString());
|
||||
dict.Add("DeviceIdentifier", Device.Identifier);
|
||||
dict.Add("DeviceName", Device.Name);
|
||||
dict.Add("DevicePushToken", Device.PushToken);
|
||||
}
|
||||
|
||||
if(Token != null && Provider.HasValue)
|
||||
{
|
||||
dict.Add("TwoFactorToken", Token);
|
||||
dict.Add("TwoFactorProvider", Provider.Value.ToString());
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Bit.App.Models.Api
|
||||
{
|
||||
public class TokenTwoFactorRequest
|
||||
{
|
||||
public string Code { get; set; }
|
||||
public string Provider { get; set; }
|
||||
public DeviceRequest Device { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
namespace Bit.App.Models.Api
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.App.Models.Api
|
||||
{
|
||||
public class TokenResponse
|
||||
{
|
||||
public string Token { get; set; }
|
||||
public ProfileResponse Profile { get; set; }
|
||||
[JsonProperty("access_token")]
|
||||
public string AccessToken { get; set; }
|
||||
[JsonProperty("expires_in")]
|
||||
public long ExpiresIn { get; set; }
|
||||
[JsonProperty("refresh_token")]
|
||||
public string RefreshToken { get; set; }
|
||||
[JsonProperty("token_type")]
|
||||
public string TokenType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private ICryptoService _cryptoService;
|
||||
private IAuthService _authService;
|
||||
private ITokenService _tokenService;
|
||||
private IDeviceInfoService _deviceInfoService;
|
||||
private IAppIdService _appIdService;
|
||||
private IUserDialogs _userDialogs;
|
||||
@@ -32,6 +33,7 @@ namespace Bit.App.Pages
|
||||
_email = email;
|
||||
_cryptoService = Resolver.Resolve<ICryptoService>();
|
||||
_authService = Resolver.Resolve<IAuthService>();
|
||||
_tokenService = Resolver.Resolve<ITokenService>();
|
||||
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
||||
_appIdService = Resolver.Resolve<IAppIdService>();
|
||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||
@@ -196,9 +198,10 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
_cryptoService.Key = key;
|
||||
_authService.Token = response.Result.Token;
|
||||
_authService.UserId = response.Result?.Profile?.Id;
|
||||
_authService.Email = response.Result?.Profile?.Email;
|
||||
_tokenService.Token = response.Result.AccessToken;
|
||||
_tokenService.RefreshToken = response.Result.RefreshToken;
|
||||
_authService.UserId = _tokenService.TokenUserId;
|
||||
_authService.Email = _tokenService.TokenEmail;
|
||||
_settings.AddOrUpdateValue(Constants.LastLoginEmail, _authService.Email);
|
||||
_googleAnalyticsService.RefreshUserId();
|
||||
_googleAnalyticsService.TrackAppEvent("LoggedIn");
|
||||
@@ -208,7 +211,7 @@ namespace Bit.App.Pages
|
||||
_pushNotification.Register();
|
||||
}
|
||||
|
||||
if(_authService.IsAuthenticatedTwoFactor)
|
||||
if(false) // TODO: 2FA
|
||||
{
|
||||
await Navigation.PushAsync(new LoginTwoFactorPage());
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private ICryptoService _cryptoService;
|
||||
private IAuthService _authService;
|
||||
private ITokenService _tokenService;
|
||||
private IDeviceInfoService _deviceInfoService;
|
||||
private IAppIdService _appIdService;
|
||||
private IUserDialogs _userDialogs;
|
||||
@@ -25,6 +26,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
_cryptoService = Resolver.Resolve<ICryptoService>();
|
||||
_authService = Resolver.Resolve<IAuthService>();
|
||||
_tokenService = Resolver.Resolve<ITokenService>();
|
||||
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
||||
_appIdService = Resolver.Resolve<IAppIdService>();
|
||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||
@@ -134,15 +136,16 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
var request = new TokenTwoFactorRequest
|
||||
var request = new TokenRequest
|
||||
{
|
||||
Code = CodeCell.Entry.Text.Replace(" ", ""),
|
||||
Provider = "Authenticator",
|
||||
// TODO: username and pass from previous page
|
||||
Token = CodeCell.Entry.Text.Replace(" ", ""),
|
||||
Provider = 0,
|
||||
Device = new DeviceRequest(_appIdService, _deviceInfoService)
|
||||
};
|
||||
|
||||
_userDialogs.ShowLoading(AppResources.ValidatingCode, MaskType.Black);
|
||||
var response = await _authService.TokenTwoFactorPostAsync(request);
|
||||
var response = await _authService.TokenPostAsync(request);
|
||||
_userDialogs.HideLoading();
|
||||
if(!response.Succeeded)
|
||||
{
|
||||
@@ -150,9 +153,10 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
_authService.Token = response.Result.Token;
|
||||
_authService.UserId = response.Result.Profile.Id;
|
||||
_authService.Email = response.Result.Profile.Email;
|
||||
_tokenService.Token = response.Result.AccessToken;
|
||||
_tokenService.RefreshToken = response.Result.RefreshToken;
|
||||
_authService.UserId = _tokenService.TokenUserId;
|
||||
_authService.Email = _tokenService.TokenEmail;
|
||||
|
||||
var task = Task.Run(async () => await _syncService.FullSyncAsync());
|
||||
Application.Current.MainPage = new MainPage();
|
||||
|
||||
@@ -9,15 +9,15 @@ using System.Net;
|
||||
|
||||
namespace Bit.App.Repositories
|
||||
{
|
||||
public class AuthApiRepository : BaseApiRepository, IAuthApiRepository
|
||||
public class ConnectApiRepository : BaseApiRepository, IConnectApiRepository
|
||||
{
|
||||
public AuthApiRepository(
|
||||
public ConnectApiRepository(
|
||||
IConnectivity connectivity,
|
||||
IHttpService httpService)
|
||||
: base(connectivity, httpService)
|
||||
{ }
|
||||
|
||||
protected override string ApiRoute => "auth";
|
||||
protected override string ApiRoute => "connect";
|
||||
|
||||
public virtual async Task<ApiResult<TokenResponse>> PostTokenAsync(TokenRequest requestObj)
|
||||
{
|
||||
@@ -28,44 +28,11 @@ namespace Bit.App.Repositories
|
||||
|
||||
using(var client = HttpService.Client)
|
||||
{
|
||||
var requestMessage = new TokenHttpRequestMessage(requestObj)
|
||||
var requestMessage = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/token")),
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var response = await client.SendAsync(requestMessage).ConfigureAwait(false);
|
||||
if(!response.IsSuccessStatusCode)
|
||||
{
|
||||
return await HandleErrorAsync<TokenResponse>(response).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
var responseObj = JsonConvert.DeserializeObject<TokenResponse>(responseContent);
|
||||
return ApiResult<TokenResponse>.Success(responseObj, response.StatusCode);
|
||||
}
|
||||
catch(WebException e)
|
||||
{
|
||||
return HandledWebException<TokenResponse>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task<ApiResult<TokenResponse>> PostTokenTwoFactorAsync(TokenTwoFactorRequest requestObj)
|
||||
{
|
||||
if(!Connectivity.IsConnected)
|
||||
{
|
||||
return HandledNotConnected<TokenResponse>();
|
||||
}
|
||||
|
||||
using(var client = HttpService.Client)
|
||||
{
|
||||
var requestMessage = new TokenHttpRequestMessage(requestObj)
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/token/two-factor")),
|
||||
Content = new FormUrlEncodedContent(requestObj.ToIdentityTokenRequest())
|
||||
};
|
||||
|
||||
try
|
||||
@@ -9,18 +9,17 @@ namespace Bit.App.Services
|
||||
{
|
||||
public class AuthService : IAuthService
|
||||
{
|
||||
private const string TokenKey = "token";
|
||||
private const string EmailKey = "email";
|
||||
private const string UserIdKey = "userId";
|
||||
private const string PreviousUserIdKey = "previousUserId";
|
||||
private const string PinKey = "pin";
|
||||
|
||||
private readonly ISecureStorageService _secureStorage;
|
||||
private readonly ITokenService _tokenService;
|
||||
private readonly ISettings _settings;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IAuthApiRepository _authApiRepository;
|
||||
private readonly IConnectApiRepository _connectApiRepository;
|
||||
|
||||
private string _token;
|
||||
private string _email;
|
||||
private string _userId;
|
||||
private string _previousUserId;
|
||||
@@ -28,48 +27,16 @@ namespace Bit.App.Services
|
||||
|
||||
public AuthService(
|
||||
ISecureStorageService secureStorage,
|
||||
ITokenService tokenService,
|
||||
ISettings settings,
|
||||
ICryptoService cryptoService,
|
||||
IAuthApiRepository authApiRepository)
|
||||
IConnectApiRepository connectApiRepository)
|
||||
{
|
||||
_secureStorage = secureStorage;
|
||||
_tokenService = tokenService;
|
||||
_settings = settings;
|
||||
_cryptoService = cryptoService;
|
||||
_authApiRepository = authApiRepository;
|
||||
}
|
||||
|
||||
public string Token
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_token != null)
|
||||
{
|
||||
return _token;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(TokenKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_token = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _token;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(TokenKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(TokenKey);
|
||||
}
|
||||
|
||||
_token = value;
|
||||
}
|
||||
_connectApiRepository = connectApiRepository;
|
||||
}
|
||||
|
||||
public string UserId
|
||||
@@ -170,14 +137,8 @@ namespace Bit.App.Services
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cryptoService.Key != null && !string.IsNullOrWhiteSpace(Token) && !string.IsNullOrWhiteSpace(UserId);
|
||||
}
|
||||
}
|
||||
public bool IsAuthenticatedTwoFactor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cryptoService.Key != null && !string.IsNullOrWhiteSpace(Token) && string.IsNullOrWhiteSpace(UserId);
|
||||
return _cryptoService.Key != null && !string.IsNullOrWhiteSpace(_tokenService.Token) &&
|
||||
!string.IsNullOrWhiteSpace(UserId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +178,9 @@ namespace Bit.App.Services
|
||||
|
||||
public void LogOut()
|
||||
{
|
||||
Token = null;
|
||||
_tokenService.Token = null;
|
||||
_tokenService.RefreshToken = null;
|
||||
_tokenService.AuthBearer = null;
|
||||
UserId = null;
|
||||
Email = null;
|
||||
_cryptoService.Key = null;
|
||||
@@ -227,13 +190,7 @@ namespace Bit.App.Services
|
||||
public async Task<ApiResult<TokenResponse>> TokenPostAsync(TokenRequest request)
|
||||
{
|
||||
// TODO: move more logic in here
|
||||
return await _authApiRepository.PostTokenAsync(request);
|
||||
}
|
||||
|
||||
public async Task<ApiResult<TokenResponse>> TokenTwoFactorPostAsync(TokenTwoFactorRequest request)
|
||||
{
|
||||
// TODO: move more logic in here
|
||||
return await _authApiRepository.PostTokenTwoFactorAsync(request);
|
||||
return await _connectApiRepository.PostTokenAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
210
src/App/Services/TokenService.cs
Normal file
210
src/App/Services/TokenService.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using System;
|
||||
using Bit.App.Abstractions;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
private const string TokenKey = "accessToken";
|
||||
private const string RefreshTokenKey = "refreshToken";
|
||||
private const string AuthBearerKey = "token";
|
||||
|
||||
private readonly ISecureStorageService _secureStorage;
|
||||
|
||||
private string _token;
|
||||
private dynamic _decodedToken;
|
||||
private string _refreshToken;
|
||||
private string _authBearer;
|
||||
|
||||
private static readonly DateTime _epoc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public TokenService(ISecureStorageService secureStorage)
|
||||
{
|
||||
_secureStorage = secureStorage;
|
||||
}
|
||||
|
||||
public string Token
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_token != null)
|
||||
{
|
||||
return _token;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(TokenKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_token = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _token;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(TokenKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(TokenKey);
|
||||
RefreshToken = null;
|
||||
AuthBearer = null;
|
||||
}
|
||||
|
||||
_decodedToken = null;
|
||||
_token = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime TokenExpiration
|
||||
{
|
||||
get
|
||||
{
|
||||
var decoded = DecodeToken();
|
||||
long exp = 0;
|
||||
if(decoded?.exp != null || !long.TryParse(decoded.exp, out exp))
|
||||
{
|
||||
throw new InvalidOperationException("No exp in token.");
|
||||
}
|
||||
|
||||
return _epoc.AddSeconds(Convert.ToDouble(exp));
|
||||
}
|
||||
}
|
||||
|
||||
public bool TokenExpired => DateTime.UtcNow < TokenExpiration;
|
||||
public TimeSpan TokenTimeRemaining => TokenExpiration - DateTime.UtcNow;
|
||||
public bool TokenNeedseRefresh => TokenTimeRemaining.TotalMinutes < 5;
|
||||
public string TokenUserId => DecodeToken()?.sub;
|
||||
public string TokenEmail => DecodeToken()?.email;
|
||||
public string TokenName => DecodeToken()?.name;
|
||||
|
||||
public string RefreshToken
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_refreshToken != null)
|
||||
{
|
||||
return _refreshToken;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(RefreshTokenKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_refreshToken = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _refreshToken;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(RefreshTokenKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(RefreshTokenKey);
|
||||
}
|
||||
|
||||
_refreshToken = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string AuthBearer
|
||||
{
|
||||
get
|
||||
{
|
||||
if(_authBearer != null)
|
||||
{
|
||||
return _authBearer;
|
||||
}
|
||||
|
||||
var tokenBytes = _secureStorage.Retrieve(AuthBearerKey);
|
||||
if(tokenBytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_authBearer = Encoding.UTF8.GetString(tokenBytes, 0, tokenBytes.Length);
|
||||
return _authBearer;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
var tokenBytes = Encoding.UTF8.GetBytes(value);
|
||||
_secureStorage.Store(AuthBearerKey, tokenBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
_secureStorage.Delete(AuthBearerKey);
|
||||
}
|
||||
|
||||
_authBearer = value;
|
||||
}
|
||||
}
|
||||
|
||||
public dynamic DecodeToken()
|
||||
{
|
||||
if(_decodedToken != null)
|
||||
{
|
||||
return _decodedToken;
|
||||
}
|
||||
|
||||
if(Token == null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(Token)} not found.");
|
||||
}
|
||||
|
||||
var parts = Token.Split('.');
|
||||
if(parts.Length != 3)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(Token)} must have 3 parts");
|
||||
}
|
||||
|
||||
var decodedBytes = Base64UrlDecode(parts[1]);
|
||||
if(decodedBytes == null || decodedBytes.Length < 1)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(Token)} must have 3 parts");
|
||||
}
|
||||
|
||||
_decodedToken = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(decodedBytes, 0, decodedBytes.Length));
|
||||
return _decodedToken;
|
||||
}
|
||||
|
||||
private static byte[] Base64UrlDecode(string input)
|
||||
{
|
||||
var output = input;
|
||||
// 62nd char of encoding
|
||||
output = output.Replace('-', '+');
|
||||
// 63rd char of encoding
|
||||
output = output.Replace('_', '/');
|
||||
// Pad with trailing '='s
|
||||
switch(output.Length % 4)
|
||||
{
|
||||
case 0:
|
||||
// No pad chars in this case
|
||||
break;
|
||||
case 2:
|
||||
// Two pad chars
|
||||
output += "=="; break;
|
||||
case 3:
|
||||
// One pad char
|
||||
output += "="; break;
|
||||
default:
|
||||
throw new InvalidOperationException("Illegal base64url string!");
|
||||
}
|
||||
|
||||
// Standard base64 decoder
|
||||
return Convert.FromBase64String(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ namespace Bit.App
|
||||
{
|
||||
public TokenHttpRequestMessage()
|
||||
{
|
||||
var authService = Resolver.Resolve<IAuthService>();
|
||||
var tokenService = Resolver.Resolve<ITokenService>();
|
||||
var appIdService = Resolver.Resolve<IAppIdService>();
|
||||
if(!string.IsNullOrWhiteSpace(authService.Token))
|
||||
if(!string.IsNullOrWhiteSpace(tokenService.Token))
|
||||
{
|
||||
Headers.Add("Authorization", $"Bearer {authService.Token}");
|
||||
Headers.Add("Authorization", $"Bearer2 {tokenService.Token}");
|
||||
}
|
||||
if(!string.IsNullOrWhiteSpace(appIdService.AppId))
|
||||
{
|
||||
|
||||
@@ -280,12 +280,13 @@ namespace Bit.iOS.Extension
|
||||
.RegisterType<ILocalizeService, LocalizeService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILogService, LogService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IHttpService, HttpService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ITokenService, TokenService>(new ContainerControlledLifetimeManager())
|
||||
// Repositories
|
||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILoginRepository, LoginRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILoginApiRepository, LoginApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IConnectApiRepository, ConnectApiRepository>(new ContainerControlledLifetimeManager())
|
||||
// Other
|
||||
.RegisterInstance(CrossConnectivity.Current, new ContainerControlledLifetimeManager())
|
||||
.RegisterInstance(CrossFingerprint.Current, new ContainerControlledLifetimeManager());
|
||||
|
||||
@@ -264,13 +264,14 @@ namespace Bit.iOS
|
||||
.RegisterType<ILocalizeService, LocalizeService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILogService, LogService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IHttpService, HttpService>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ITokenService, TokenService>(new ContainerControlledLifetimeManager())
|
||||
// Repositories
|
||||
// Repositories
|
||||
.RegisterType<IFolderRepository, FolderRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IFolderApiRepository, FolderApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILoginRepository, LoginRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ILoginApiRepository, LoginApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAuthApiRepository, AuthApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IConnectApiRepository, ConnectApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IDeviceApiRepository, DeviceApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<IAccountsApiRepository, AccountsApiRepository>(new ContainerControlledLifetimeManager())
|
||||
.RegisterType<ICipherApiRepository, CipherApiRepository>(new ContainerControlledLifetimeManager())
|
||||
|
||||
Reference in New Issue
Block a user