Merge pull request #211 from dotnet-architecture/xamarin
Xamarin client hybrid authentication flow
This commit is contained in:
commit
5db62c14b9
@ -6,7 +6,6 @@
|
|||||||
public const string MockTag = "Mock";
|
public const string MockTag = "Mock";
|
||||||
public const string DefaultEndpoint = "http://13.88.8.119";
|
public const string DefaultEndpoint = "http://13.88.8.119";
|
||||||
|
|
||||||
|
|
||||||
private string _baseEndpoint;
|
private string _baseEndpoint;
|
||||||
private static readonly GlobalSetting _instance = new GlobalSetting();
|
private static readonly GlobalSetting _instance = new GlobalSetting();
|
||||||
|
|
||||||
@ -31,6 +30,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ClientId { get { return "xamarin"; }}
|
||||||
|
|
||||||
|
public string ClientSecret { get { return "secret"; }}
|
||||||
|
|
||||||
public string AuthToken { get; set; }
|
public string AuthToken { get; set; }
|
||||||
|
|
||||||
public string RegisterWebsite { get; set; }
|
public string RegisterWebsite { get; set; }
|
||||||
@ -47,6 +50,8 @@
|
|||||||
|
|
||||||
public string UserInfoEndpoint { get; set; }
|
public string UserInfoEndpoint { get; set; }
|
||||||
|
|
||||||
|
public string TokenEndpoint { get; set; }
|
||||||
|
|
||||||
public string LogoutEndpoint { get; set; }
|
public string LogoutEndpoint { get; set; }
|
||||||
|
|
||||||
public string IdentityCallback { get; set; }
|
public string IdentityCallback { get; set; }
|
||||||
@ -61,6 +66,7 @@
|
|||||||
BasketEndpoint = string.Format("{0}:5103", baseEndpoint);
|
BasketEndpoint = string.Format("{0}:5103", baseEndpoint);
|
||||||
IdentityEndpoint = string.Format("{0}:5105/connect/authorize", baseEndpoint);
|
IdentityEndpoint = string.Format("{0}:5105/connect/authorize", baseEndpoint);
|
||||||
UserInfoEndpoint = string.Format("{0}:5105/connect/userinfo", baseEndpoint);
|
UserInfoEndpoint = string.Format("{0}:5105/connect/userinfo", baseEndpoint);
|
||||||
|
TokenEndpoint = string.Format("{0}:5105/connect/token", baseEndpoint);
|
||||||
LogoutEndpoint = string.Format("{0}:5105/connect/endsession", baseEndpoint);
|
LogoutEndpoint = string.Format("{0}:5105/connect/endsession", baseEndpoint);
|
||||||
IdentityCallback = string.Format("{0}:5105/xamarincallback", baseEndpoint);
|
IdentityCallback = string.Format("{0}:5105/xamarincallback", baseEndpoint);
|
||||||
LogoutCallback = string.Format("{0}:5105/Account/Redirecting", baseEndpoint);
|
LogoutCallback = string.Format("{0}:5105/Account/Redirecting", baseEndpoint);
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Models.Token
|
||||||
|
{
|
||||||
|
public class UserToken
|
||||||
|
{
|
||||||
|
[JsonProperty("id_token")]
|
||||||
|
public string IdToken { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("access_token")]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("expires_in")]
|
||||||
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("token_type")]
|
||||||
|
public string TokenType { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("refresh_token")]
|
||||||
|
public string RefreshToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -13,11 +13,10 @@ namespace eShopOnContainers.Core.Services.Identity
|
|||||||
|
|
||||||
// Dictionary with values for the authorize request
|
// Dictionary with values for the authorize request
|
||||||
var dic = new Dictionary<string, string>();
|
var dic = new Dictionary<string, string>();
|
||||||
dic.Add("client_id", "xamarin");
|
dic.Add("client_id", GlobalSetting.Instance.ClientId);
|
||||||
dic.Add("client_secret", "secret");
|
dic.Add("client_secret", GlobalSetting.Instance.ClientSecret);
|
||||||
dic.Add("response_type", "code id_token token");
|
dic.Add("response_type", "code id_token");
|
||||||
dic.Add("scope", "openid profile basket orders offline_access");
|
dic.Add("scope", "openid profile basket orders offline_access");
|
||||||
|
|
||||||
dic.Add("redirect_uri", GlobalSetting.Instance.IdentityCallback);
|
dic.Add("redirect_uri", GlobalSetting.Instance.IdentityCallback);
|
||||||
dic.Add("nonce", Guid.NewGuid().ToString("N"));
|
dic.Add("nonce", Guid.NewGuid().ToString("N"));
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
|
|
||||||
Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "");
|
Task<TResult> PostAsync<TResult>(string uri, TResult data, string token = "", string header = "");
|
||||||
|
|
||||||
|
Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret);
|
||||||
|
|
||||||
Task DeleteAsync(string uri, string token = "");
|
Task DeleteAsync(string uri, string token = "");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -61,6 +61,28 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<TResult> PostAsync<TResult>(string uri, string data, string clientId, string clientSecret)
|
||||||
|
{
|
||||||
|
HttpClient httpClient = CreateHttpClient(string.Empty);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(clientSecret))
|
||||||
|
{
|
||||||
|
AddBasicAuthenticationHeader(httpClient, clientId, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = new StringContent(data);
|
||||||
|
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
|
||||||
|
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
|
||||||
|
|
||||||
|
await HandleResponse(response);
|
||||||
|
string serialized = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
TResult result = await Task.Run(() =>
|
||||||
|
JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(string uri, string token = "")
|
public async Task DeleteAsync(string uri, string token = "")
|
||||||
{
|
{
|
||||||
HttpClient httpClient = CreateHttpClient(token);
|
HttpClient httpClient = CreateHttpClient(token);
|
||||||
@ -90,6 +112,17 @@ namespace eShopOnContainers.Core.Services.RequestProvider
|
|||||||
httpClient.DefaultRequestHeaders.Add(parameter, Guid.NewGuid().ToString());
|
httpClient.DefaultRequestHeaders.Add(parameter, Guid.NewGuid().ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddBasicAuthenticationHeader(HttpClient httpClient, string clientId, string clientSecret)
|
||||||
|
{
|
||||||
|
if (httpClient == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret))
|
||||||
|
return;
|
||||||
|
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(clientId, clientSecret);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task HandleResponse(HttpResponseMessage response)
|
private async Task HandleResponse(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
using eShopOnContainers.Core.Models.Token;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Services.Token
|
||||||
|
{
|
||||||
|
public interface ITokenService
|
||||||
|
{
|
||||||
|
Task<UserToken> GetTokenAsync(string code);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using eShopOnContainers.Core.Services.RequestProvider;
|
||||||
|
using eShopOnContainers.Core.Models.Token;
|
||||||
|
|
||||||
|
namespace eShopOnContainers.Core.Services.Token
|
||||||
|
{
|
||||||
|
public class TokenService : ITokenService
|
||||||
|
{
|
||||||
|
private readonly IRequestProvider _requestProvider;
|
||||||
|
|
||||||
|
public TokenService(IRequestProvider requestProvider)
|
||||||
|
{
|
||||||
|
_requestProvider = requestProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserToken> GetTokenAsync(string code)
|
||||||
|
{
|
||||||
|
string data = string.Format("grant_type=authorization_code&code={0}&redirect_uri={1}", code, WebUtility.UrlEncode(GlobalSetting.Instance.IdentityCallback));
|
||||||
|
var token = await _requestProvider.PostAsync<UserToken>(GlobalSetting.Instance.TokenEndpoint, data, GlobalSetting.Instance.ClientId, GlobalSetting.Instance.ClientSecret);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ using eShopOnContainers.Core.Services.OpenUrl;
|
|||||||
using eShopOnContainers.Core.Services.RequestProvider;
|
using eShopOnContainers.Core.Services.RequestProvider;
|
||||||
using eShopOnContainers.Core.Services.Basket;
|
using eShopOnContainers.Core.Services.Basket;
|
||||||
using eShopOnContainers.Core.Services.Identity;
|
using eShopOnContainers.Core.Services.Identity;
|
||||||
|
using eShopOnContainers.Core.Services.Token;
|
||||||
using eShopOnContainers.Core.Services.Order;
|
using eShopOnContainers.Core.Services.Order;
|
||||||
using eShopOnContainers.Core.Services.User;
|
using eShopOnContainers.Core.Services.User;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
@ -53,6 +54,7 @@ namespace eShopOnContainers.Core.ViewModels.Base
|
|||||||
builder.RegisterType<DialogService>().As<IDialogService>();
|
builder.RegisterType<DialogService>().As<IDialogService>();
|
||||||
builder.RegisterType<OpenUrlService>().As<IOpenUrlService>();
|
builder.RegisterType<OpenUrlService>().As<IOpenUrlService>();
|
||||||
builder.RegisterType<IdentityService>().As<IIdentityService>();
|
builder.RegisterType<IdentityService>().As<IIdentityService>();
|
||||||
|
builder.RegisterType<TokenService>().As<ITokenService>();
|
||||||
builder.RegisterType<RequestProvider>().As<IRequestProvider>();
|
builder.RegisterType<RequestProvider>().As<IRequestProvider>();
|
||||||
builder.RegisterType<LocationService>().As<ILocationService>().SingleInstance();
|
builder.RegisterType<LocationService>().As<ILocationService>().SingleInstance();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using eShopOnContainers.Core.Helpers;
|
using eShopOnContainers.Core.Helpers;
|
||||||
using eShopOnContainers.Core.Models.User;
|
using eShopOnContainers.Core.Models.User;
|
||||||
using eShopOnContainers.Core.Services.Identity;
|
using eShopOnContainers.Core.Services.Identity;
|
||||||
|
using eShopOnContainers.Core.Services.Token;
|
||||||
using eShopOnContainers.Core.Services.OpenUrl;
|
using eShopOnContainers.Core.Services.OpenUrl;
|
||||||
using eShopOnContainers.Core.Validations;
|
using eShopOnContainers.Core.Validations;
|
||||||
using eShopOnContainers.Core.ViewModels.Base;
|
using eShopOnContainers.Core.ViewModels.Base;
|
||||||
@ -24,13 +25,16 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
|
|
||||||
private IOpenUrlService _openUrlService;
|
private IOpenUrlService _openUrlService;
|
||||||
private IIdentityService _identityService;
|
private IIdentityService _identityService;
|
||||||
|
private ITokenService _tokenService;
|
||||||
|
|
||||||
public LoginViewModel(
|
public LoginViewModel(
|
||||||
IOpenUrlService openUrlService,
|
IOpenUrlService openUrlService,
|
||||||
IIdentityService identityService)
|
IIdentityService identityService,
|
||||||
|
ITokenService tokenService)
|
||||||
{
|
{
|
||||||
_openUrlService = openUrlService;
|
_openUrlService = openUrlService;
|
||||||
_identityService = identityService;
|
_identityService = identityService;
|
||||||
|
_tokenService = tokenService;
|
||||||
|
|
||||||
_userName = new ValidatableObject<string>();
|
_userName = new ValidatableObject<string>();
|
||||||
_password = new ValidatableObject<string>();
|
_password = new ValidatableObject<string>();
|
||||||
@ -203,7 +207,6 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
private void Logout()
|
private void Logout()
|
||||||
{
|
{
|
||||||
var authIdToken = Settings.AuthIdToken;
|
var authIdToken = Settings.AuthIdToken;
|
||||||
|
|
||||||
var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);
|
var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(logoutRequest))
|
if (!string.IsNullOrEmpty(logoutRequest))
|
||||||
@ -233,12 +236,14 @@ namespace eShopOnContainers.Core.ViewModels
|
|||||||
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
|
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
|
||||||
{
|
{
|
||||||
var authResponse = new AuthorizeResponse(url);
|
var authResponse = new AuthorizeResponse(url);
|
||||||
|
if (!string.IsNullOrWhiteSpace(authResponse.Code))
|
||||||
|
{
|
||||||
|
var userToken = await _tokenService.GetTokenAsync(authResponse.Code);
|
||||||
|
string accessToken = userToken.AccessToken;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(authResponse.AccessToken))
|
if (!string.IsNullOrWhiteSpace(accessToken))
|
||||||
{
|
{
|
||||||
if (authResponse.AccessToken != null)
|
Settings.AuthAccessToken = accessToken;
|
||||||
{
|
|
||||||
Settings.AuthAccessToken = authResponse.AccessToken;
|
|
||||||
Settings.AuthIdToken = authResponse.IdentityToken;
|
Settings.AuthIdToken = authResponse.IdentityToken;
|
||||||
|
|
||||||
await NavigationService.NavigateToAsync<MainViewModel>();
|
await NavigationService.NavigateToAsync<MainViewModel>();
|
||||||
|
@ -169,6 +169,9 @@
|
|||||||
<Compile Include="Converters\FirstValidationErrorConverter.cs" />
|
<Compile Include="Converters\FirstValidationErrorConverter.cs" />
|
||||||
<Compile Include="Effects\EntryLineColorEffect.cs" />
|
<Compile Include="Effects\EntryLineColorEffect.cs" />
|
||||||
<Compile Include="Behaviors\LineColorBehavior.cs" />
|
<Compile Include="Behaviors\LineColorBehavior.cs" />
|
||||||
|
<Compile Include="Models\Token\UserToken.cs" />
|
||||||
|
<Compile Include="Services\Token\TokenService.cs" />
|
||||||
|
<Compile Include="Services\Token\ITokenService.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="app.config" />
|
<None Include="app.config" />
|
||||||
@ -262,6 +265,10 @@
|
|||||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\Token\" />
|
||||||
|
<Folder Include="Services\Token\" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Xamarin.Forms": "2.3.4.231",
|
"Xamarin.Forms": "2.3.4.231",
|
||||||
"xunit": "2.2.0"
|
"xunit": "2.2.0",
|
||||||
|
"xunit.runner.console": "2.2.0"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
".NETPortable,Version=v4.5,Profile=Profile111": {}
|
".NETPortable,Version=v4.5,Profile=Profile111": {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user