Skip to content
This repository has been archived by the owner on Jun 13, 2024. It is now read-only.

Commit

Permalink
Improve fingerprint integration in the Xamarin iOS app
Browse files Browse the repository at this point in the history
  • Loading branch information
Ibon committed Apr 8, 2016
1 parent b7bc664 commit 0b7e41a
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,9 @@ namespace MyHealth.Client.Core.Messages
{
public class LoggedUserInfoChangedMessage : MvxMessage
{
private readonly string _User;
private readonly string _Email;
private readonly byte[] _Photo;

public string User { get { return _User; } }
public string Email { get { return _Email; } }
public byte[] Photo { get { return _Photo; } }

public LoggedUserInfoChangedMessage(object sender, string user, string email, byte[] photo)
public LoggedUserInfoChangedMessage(object sender)
: base(sender)
{
_User = user;
_Email = email;
_Photo = photo;
}
}
}
Expand Down
61 changes: 26 additions & 35 deletions src/MyHealth.Client.Core/ServiceAgents/AuthenticationService.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Linq;
using System.Threading.Tasks;
using System.Linq;
using MyHealth.Client.Core.Helpers;
using MvvmCross.Plugins.Messenger;
using Cirrious.CrossCore;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using MvvmCross.Plugins.Messenger;
using MyHealth.Client.Core.Helpers;
using MyHealth.Client.Core.Messages;

namespace MyHealth.Client.Core.ServiceAgents
{
public class AuthenticationService
{
private PatientsService _patientService = null;
IMvxMessenger _messenger = null;
private const int GetOnlyOne = 1;
private readonly PatientsService _patientService;
private readonly IMvxMessenger _messenger;

public AuthenticationService(PatientsService patientService, IMvxMessenger messenger)
{
Expand All @@ -22,41 +21,33 @@ public AuthenticationService(PatientsService patientService, IMvxMessenger messe

public async Task SignInAsync(IPlatformParameters context)
{
if (Settings.SecurityEnabled)
await MicrosoftGraphService.SignInAsync(context);
var currentPatient = await GetPatientInfoAsync();

if (currentPatient != null)
{
await MicrosoftGraphService.SignInAsync(context);

Model.Patient currentPatient = await GetPatientInfo();
if (currentPatient != null)
{
AppSettings.CurrentPatientId = currentPatient.PatientId;
MicrosoftGraphService.LoggedUserPhoto = currentPatient.Picture;
}

PublishChanges();
LoadUserPhoto();
AppSettings.CurrentPatientId = currentPatient.PatientId;
// We first get patient's picture from backend
MicrosoftGraphService.LoggedUserPhoto = currentPatient.Picture;
// And, then, try to get a newer one from Microsoft Graph
MicrosoftGraphService.LoggedUserPhoto = await MicrosoftGraphService.GetUserPhotoAsync();
}
}

private async void LoadUserPhoto()
{
MicrosoftGraphService.LoggedUserPhoto = await MicrosoftGraphService.GetUserPhotoAsync();
PublishChanges();
_messenger.Publish(new LoggedUserInfoChangedMessage(this));
}

private void PublishChanges()
{
_messenger.Publish(new LoggedUserInfoChangedMessage(this,
user: MicrosoftGraphService.LoggedUser,
email: MicrosoftGraphService.LoggedUserEmail,
photo: MicrosoftGraphService.LoggedUserPhoto));
}
public void SignOut ()
{
MicrosoftGraphService.SignOut ();
}

private async Task<Model.Patient> GetPatientInfo()
private async Task<Model.Patient> GetPatientInfoAsync()
{
return (await _patientService
.GetByNameAsync(MicrosoftGraphService.LoggedUser, GetOnlyOne))
.FirstOrDefault();
var patientsMatchingName = await _patientService
.GetByNameAsync (MicrosoftGraphService.LoggedUser, 1);
var patient = patientsMatchingName.FirstOrDefault();

return patient;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ public interface IMyHealthClient
HomeAppointmentsService HomeAppointmentsService { get; }

DoctorCalendarService DoctorCalendarService { get; }

AuthenticationService AuthenticationService { get; }
}
}
171 changes: 107 additions & 64 deletions src/MyHealth.Client.Core/ServiceAgents/MicrosoftGraphService.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using MyHealth.Client.Core.Helpers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;

namespace MyHealth.Client.Core
{
Expand All @@ -21,15 +22,14 @@ public class MicrosoftGraphService

private static IPlatformParameters authenticationParentUiContext;
private static GraphService graphClient = null;

public static string AccessToken = null;
private static string accessToken = null;

#region Properties

public static AuthenticationContext AuthenticationContext { get; private set; }
public static string LastTenantId { get; set; }
public static string LoggedUser { get; private set; }
public static string LoggedUserEmail { get; private set; }
public static string LoggedUser { get; set; }
public static string LoggedUserEmail { get; set; }
public static byte[] LoggedUserPhoto { get; set; }

#endregion Properties
Expand All @@ -39,19 +39,34 @@ public class MicrosoftGraphService
public static async Task SignInAsync(IPlatformParameters context)
{
authenticationParentUiContext = context;
if (AppSettings.OutlookIntegration)
await GetGraphClientAsync();
else
await SignInAsync();

if (AppSettings.OutlookIntegration) {
await GetGraphClientAsync ();
return;
}

await SignInAsync ();
}

public static void SignOut ()
{
AuthenticationContext?.TokenCache.Clear ();

accessToken = null;

LastTenantId = null;
LoggedUser = null;
LoggedUserEmail = null;
LoggedUserPhoto = null;
}

// Create an event on signed-in user's default calendar using the REST api.
public static async Task AddEventAsync(
string subject, DateTimeOffset startTime, DateTimeOffset endTime,
IEnumerable<string> attendeeEmails, string description,string locationDisplayName) {
await EnsureAccessTokenAsync();

if (!string.IsNullOrEmpty(AccessToken))
if (!string.IsNullOrEmpty(accessToken))
{
var @event = new
{
Expand Down Expand Up @@ -81,7 +96,7 @@ public static async Task AddEventAsync(
var postData = JsonConvert.SerializeObject(@event);
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(postData, Encoding.UTF8, "application/json");
var response = await client.PostAsync(api, content);
Expand All @@ -106,7 +121,7 @@ public static async Task AddEventAsync(
List<Office365.Event> result = new List<Office365.Event>();
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(api);
var responseString = await response.Content.ReadAsStringAsync();
Expand All @@ -133,7 +148,7 @@ public static async Task<byte[]> GetUserPhotoAsync()
string url = "https://graph.microsoft.com/v1.0/me/photo/$value";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var stream = await client.GetStreamAsync(url);
result = ReadStream(stream);
}
Expand All @@ -146,50 +161,74 @@ public static async Task<byte[]> GetUserPhotoAsync()
return result;
}

static byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}

#endregion Public Methods

#region Private Methods

private async static Task<string> GetAccessTokenAsync()
static byte[] ReadStream(Stream input)
{
byte[] buffer = new byte[16 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}

private async static Task RetrieveAccessTokenAndUserInfoAsync()
{
if (authenticationParentUiContext == null)
{
throw new InvalidOperationException("The authentication parent is invalid");
}

AuthenticationResult result = await AuthenticationContext.AcquireTokenAsync(
resource: AppSettings.Security.Scope,
clientId: AppSettings.Security.ClientId,
redirectUri: AppSettings.Security.RedirectUri,
parameters: authenticationParentUiContext);

if (!string.IsNullOrEmpty(result.AccessToken))
{
AccessToken = result.AccessToken;
LoggedUser = $"{ result.UserInfo.GivenName} {result.UserInfo.FamilyName}";
LoggedUserEmail = result.UserInfo.DisplayableId;
LastTenantId = result.TenantId;
}
else
{
AccessToken = null;
}

return AccessToken;
var errorAcquiringToken = false;
AuthenticationResult result = null;

try
{
if (accessToken == null)
throw new Exception ("There was previously a sign out. We need to sign in again");

result = await AuthenticationContext.AcquireTokenSilentAsync(
resource: AppSettings.Security.Scope,
clientId: AppSettings.Security.ClientId);

if (result != null && !string.IsNullOrWhiteSpace(result.AccessToken))
accessToken = result.AccessToken;
}
catch (Exception)
{
errorAcquiringToken = true;
}

if (errorAcquiringToken)
{
try
{
result = await AuthenticationContext.AcquireTokenAsync(
resource: AppSettings.Security.Scope,
clientId: AppSettings.Security.ClientId,
redirectUri: AppSettings.Security.RedirectUri,
parameters: authenticationParentUiContext);

if (result != null && !string.IsNullOrWhiteSpace(result.AccessToken))
accessToken = result.AccessToken;
}
catch (Exception e)
{
throw;
}
}

LoggedUser = $"{result.UserInfo.GivenName} {result.UserInfo.FamilyName}";
LoggedUserEmail = result.UserInfo.DisplayableId;

LastTenantId = result.TenantId;
}

private static bool IsValidDomain(string email)
Expand All @@ -199,26 +238,24 @@ private static bool IsValidDomain(string email)

private static async Task EnsureAccessTokenAsync()
{
if (AuthenticationContext == null)
{
AuthenticationContext = new AuthenticationContext(AppSettings.Security.Authority, new TokenCache());
}
SignOut ();

if (string.IsNullOrEmpty(AccessToken))
if (string.IsNullOrEmpty(accessToken))
{
await GetAccessTokenAsync();
await RetrieveAccessTokenAndUserInfoAsync();
}
}



static async Task SignInAsync()
{
var errorAuthenticating = false;

try
{
AuthenticationContext = new AuthenticationContext(AppSettings.Security.Authority, new TokenCache());
AccessToken = await GetAccessTokenAsync();
if (AuthenticationContext == null)
AuthenticationContext = new AuthenticationContext(AppSettings.Security.Authority);

await RetrieveAccessTokenAndUserInfoAsync();
}
catch (AdalException ex) when (ex.ErrorCode == AdalError.AuthenticationCanceled)
{
Expand All @@ -241,16 +278,22 @@ static async Task SignInAsync()
static async Task<GraphService> GetGraphClientAsync()
{
var errorAuthenticating = false;

try
{
await SignInAsync();

if (!string.IsNullOrEmpty(AccessToken) && IsValidDomain(LoggedUserEmail)
if (!string.IsNullOrEmpty(accessToken) && IsValidDomain(LoggedUserEmail)
&& AppSettings.OutlookIntegration)
{
var tenantId = (LastTenantId ?? OutlookTenandId) + UriSchemeDelimiter;
var serviceRoot = new Uri("https://graph.microsoft.com/beta/" + tenantId);
graphClient = new GraphService(serviceRoot, async () => await GetAccessTokenAsync());
graphClient = new GraphService(serviceRoot, async () =>
{
await RetrieveAccessTokenAndUserInfoAsync();
return accessToken;
});
}
}
catch (Exception)
Expand Down
Loading

0 comments on commit 0b7e41a

Please sign in to comment.