mirror of
https://github.com/bitwarden/mobile.git
synced 2024-11-24 11:55:38 +01:00
stub out beginnings of apiservice
This commit is contained in:
parent
0d417b3eee
commit
579a7e0398
@ -1,5 +1,6 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -8,7 +9,7 @@ using Xamarin.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
public class MobilePlatformUtilsService
|
public class MobilePlatformUtilsService : IPlatformUtilsService
|
||||||
{
|
{
|
||||||
private static readonly Random _random = new Random();
|
private static readonly Random _random = new Random();
|
||||||
|
|
||||||
|
28
src/Core/Abstractions/IPlatformUtilsService.cs
Normal file
28
src/Core/Abstractions/IPlatformUtilsService.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Abstractions
|
||||||
|
{
|
||||||
|
public interface IPlatformUtilsService
|
||||||
|
{
|
||||||
|
string IdentityClientId { get; }
|
||||||
|
|
||||||
|
Task CopyToClipboardAsync(string text, Dictionary<string, object> options = null);
|
||||||
|
string GetApplicationVersion();
|
||||||
|
DeviceType GetDevice();
|
||||||
|
string GetDeviceString();
|
||||||
|
bool IsDev();
|
||||||
|
bool IsSelfHost();
|
||||||
|
bool IsViewOpen();
|
||||||
|
void LaunchUri(string uri, Dictionary<string, object> options = null);
|
||||||
|
int? LockTimeout();
|
||||||
|
Task<string> ReadFromClipboardAsync(Dictionary<string, object> options = null);
|
||||||
|
void SaveFile();
|
||||||
|
Task<bool> ShowDialogAsync(string text, string title = null, string confirmText = null,
|
||||||
|
string cancelText = null, string type = null);
|
||||||
|
void ShowToast(string type, string title, string text, Dictionary<string, object> options = null);
|
||||||
|
void ShowToast(string type, string title, string[] text, Dictionary<string, object> options = null);
|
||||||
|
bool SupportsU2f();
|
||||||
|
}
|
||||||
|
}
|
14
src/Core/Exceptions/ApiException.cs
Normal file
14
src/Core/Exceptions/ApiException.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using Bit.Core.Models.Response;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core.Exceptions
|
||||||
|
{
|
||||||
|
public class ApiException : Exception
|
||||||
|
{
|
||||||
|
public ApiException(ErrorResponse error)
|
||||||
|
: base("An API error has occurred.")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public ErrorResponse Error { get; set; }
|
||||||
|
}
|
||||||
|
}
|
9
src/Core/Models/Domain/EnvironmentUrls.cs
Normal file
9
src/Core/Models/Domain/EnvironmentUrls.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace Bit.Core.Models.Domain
|
||||||
|
{
|
||||||
|
public class EnvironmentUrls
|
||||||
|
{
|
||||||
|
public string Base { get; set; }
|
||||||
|
public string Api { get; set; }
|
||||||
|
public string Identity { get; set; }
|
||||||
|
}
|
||||||
|
}
|
21
src/Core/Models/Request/DeviceRequestModels.cs
Normal file
21
src/Core/Models/Request/DeviceRequestModels.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Request
|
||||||
|
{
|
||||||
|
public class DeviceRequest
|
||||||
|
{
|
||||||
|
public DeviceRequest(string appId, IPlatformUtilsService platformUtilsService)
|
||||||
|
{
|
||||||
|
Type = platformUtilsService.GetDevice();
|
||||||
|
Name = platformUtilsService.GetDeviceString();
|
||||||
|
Identifier = appId;
|
||||||
|
PushToken = null; // TODO?
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeviceType? Type { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
public string PushToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
src/Core/Models/Request/TokenRequest.cs
Normal file
17
src/Core/Models/Request/TokenRequest.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Request
|
||||||
|
{
|
||||||
|
public class TokenRequest
|
||||||
|
{
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string MasterPasswordHash { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
public TwoFactorProviderType Provider { get; set; }
|
||||||
|
public bool Remember { get; set; }
|
||||||
|
public DeviceRequest Device { get; set; }
|
||||||
|
}
|
||||||
|
}
|
62
src/Core/Models/Response/ErrorResponse.cs
Normal file
62
src/Core/Models/Response/ErrorResponse.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Response
|
||||||
|
{
|
||||||
|
public class ErrorResponse
|
||||||
|
{
|
||||||
|
public ErrorResponse(JObject response, HttpStatusCode status, bool identityResponse = false)
|
||||||
|
{
|
||||||
|
JObject errorModel = null;
|
||||||
|
if(response != null)
|
||||||
|
{
|
||||||
|
var responseErrorModel = response.GetValue("ErrorModel", StringComparison.OrdinalIgnoreCase);
|
||||||
|
if(responseErrorModel != null && identityResponse)
|
||||||
|
{
|
||||||
|
errorModel = responseErrorModel.Value<JObject>(); ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorModel = response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(errorModel != null)
|
||||||
|
{
|
||||||
|
Message = errorModel.GetValue("Message", StringComparison.OrdinalIgnoreCase)?.Value<string>();
|
||||||
|
ValidationErrors = errorModel.GetValue("ValidationErrors", StringComparison.OrdinalIgnoreCase)
|
||||||
|
?.Value<Dictionary<string, List<string>>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if((int)status == 429)
|
||||||
|
{
|
||||||
|
Message = "Rate limit exceeded. Try again later.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatusCode = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Message { get; set; }
|
||||||
|
public Dictionary<string, List<string>> ValidationErrors { get; set; }
|
||||||
|
public HttpStatusCode StatusCode { get; set; }
|
||||||
|
|
||||||
|
public string GetSingleMessage()
|
||||||
|
{
|
||||||
|
if(ValidationErrors == null)
|
||||||
|
{
|
||||||
|
return Message;
|
||||||
|
}
|
||||||
|
foreach(var error in ValidationErrors)
|
||||||
|
{
|
||||||
|
if(error.Value?.Any() ?? false)
|
||||||
|
{
|
||||||
|
return error.Value[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/Core/Models/Response/IdentityTokenResponse.cs
Normal file
13
src/Core/Models/Response/IdentityTokenResponse.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Bit.Core.Models.Response
|
||||||
|
{
|
||||||
|
public class IdentityTokenResponse
|
||||||
|
{
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
public string ExpiresIn { get; set; }
|
||||||
|
public string RefreshToken { get; set; }
|
||||||
|
public string TokenType { get; set; }
|
||||||
|
public string PrivateKey { get; set; }
|
||||||
|
public string Key { get; set; }
|
||||||
|
public string TwoFactorToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
11
src/Core/Models/Response/IdentityTwoFactorResponse.cs
Normal file
11
src/Core/Models/Response/IdentityTwoFactorResponse.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Response
|
||||||
|
{
|
||||||
|
public class IdentityTwoFactorResponse
|
||||||
|
{
|
||||||
|
public List<TwoFactorProviderType> TwoFactorProviders { get; set; }
|
||||||
|
public Dictionary<TwoFactorProviderType, Dictionary<string, object>> TwoFactorProviders2 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
102
src/Core/Services/ApiService.cs
Normal file
102
src/Core/Services/ApiService.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Models.Response;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class ApiService
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient = new HttpClient();
|
||||||
|
private readonly ITokenService _tokenService;
|
||||||
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
|
||||||
|
private string _deviceType;
|
||||||
|
private bool _usingBaseUrl = false;
|
||||||
|
|
||||||
|
public ApiService(
|
||||||
|
ITokenService tokenService,
|
||||||
|
IPlatformUtilsService platformUtilsService)
|
||||||
|
{
|
||||||
|
_tokenService = tokenService;
|
||||||
|
_platformUtilsService = platformUtilsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UrlsSet { get; private set; }
|
||||||
|
public string ApiBaseUrl { get; set; }
|
||||||
|
public string IdentityBaseUrl { get; set; }
|
||||||
|
|
||||||
|
public void SetUrls(EnvironmentUrls urls)
|
||||||
|
{
|
||||||
|
UrlsSet = true;
|
||||||
|
if(!string.IsNullOrWhiteSpace(urls.Base))
|
||||||
|
{
|
||||||
|
_usingBaseUrl = true;
|
||||||
|
ApiBaseUrl = urls.Base + "/api";
|
||||||
|
IdentityBaseUrl = urls.Base + "/identity";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrWhiteSpace(urls.Api) && !string.IsNullOrWhiteSpace(urls.Identity))
|
||||||
|
{
|
||||||
|
ApiBaseUrl = urls.Api;
|
||||||
|
IdentityBaseUrl = urls.Identity;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Local Dev
|
||||||
|
//ApiBaseUrl = "http://localhost:4000";
|
||||||
|
//IdentityBaseUrl = "http://localhost:33656";
|
||||||
|
// Production
|
||||||
|
ApiBaseUrl = "https://api.bitwarden.com";
|
||||||
|
IdentityBaseUrl = "https://identity.bitwarden.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Auth APIs
|
||||||
|
|
||||||
|
public async Task<Tuple<IdentityTokenResponse, IdentityTwoFactorResponse>> PostIdentityTokenAsync()
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage
|
||||||
|
{
|
||||||
|
RequestUri = new Uri(string.Concat(IdentityBaseUrl, "/connect/token")),
|
||||||
|
Method = HttpMethod.Post
|
||||||
|
};
|
||||||
|
request.Headers.Add("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
|
||||||
|
request.Headers.Add("Accept", "application/json");
|
||||||
|
request.Headers.Add("Device-Type", _deviceType);
|
||||||
|
|
||||||
|
var response = await _httpClient.SendAsync(request);
|
||||||
|
JObject responseJObject = null;
|
||||||
|
if(response.Headers.Contains("content-type") &&
|
||||||
|
response.Headers.GetValues("content-type").Any(h => h.Contains("application/json")))
|
||||||
|
{
|
||||||
|
var responseJsonString = await response.Content.ReadAsStringAsync();
|
||||||
|
responseJObject = JObject.Parse(responseJsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(responseJObject != null)
|
||||||
|
{
|
||||||
|
if(response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return new Tuple<IdentityTokenResponse, IdentityTwoFactorResponse>(
|
||||||
|
responseJObject.ToObject<IdentityTokenResponse>(), null);
|
||||||
|
}
|
||||||
|
else if(response.StatusCode == HttpStatusCode.BadRequest &&
|
||||||
|
responseJObject.ContainsKey("TwoFactorProviders2") &&
|
||||||
|
responseJObject["TwoFactorProviders2"] != null &&
|
||||||
|
responseJObject["TwoFactorProviders2"].HasValues)
|
||||||
|
{
|
||||||
|
return new Tuple<IdentityTokenResponse, IdentityTwoFactorResponse>(
|
||||||
|
null, responseJObject.ToObject<IdentityTwoFactorResponse>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ApiException(new ErrorResponse(responseJObject, response.StatusCode, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user