After install the nuget package Microsoft.IdentityModel.Clients.ActiveDirectory
I try to acquire token via
string cloud = "https://login.microsoftonline.com/common/oauth2";
string tenantId = App.tenantId;
string authority = $"{cloud}/{tenantId}";
//
string clientId = App.clientId;
Uri redirectUri = App.redirectUrl;
string resource = clientId;
AuthenticationResult authResult = null;
AuthenticationContext authContext = new AuthenticationContext(authority);
try
{
if (authContext.TokenCache.ReadItems().Count() > 0)
{
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
authResult = await authContext.AcquireTokenSilentAsync(resource, clientId);
}
else
{
authResult = await authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
}
}
catch (AdalSilentTokenAcquisitionException ee)
{
authResult = await authContext.AcquireTokenAsync(resource,clientId, redirectUri,null);
}
When i try to build i get the following errors
cannot convert from 'Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior' to 'Android.App.Activity'
cannot convert from 'Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior' to 'UIKit.UIViewController'
the line causing this is line that trigger the error
authResult = await authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
how do i solve this ?
Xamarin version is 4.0.0.482894
Microsoft.IdentityModel.Clients.ActiveDirectory version 5.1.0
Visual studio 2017
In your Shared project, use IPlatformParameter , something like this:
public class AuthenticationManager
{
private static string _authority;
private static string _resource;
private static string _clientId;
private static string _returnUri;
private static IPlatformParameters _parameters;
private string _accessToken;
public static UserInfo UserInfo { get; private set; }
public static void SetConfiguration(string authority, string resource, string clientId, string returnUri)
{
_authority = authority;
_resource = resource;
_clientId = clientId;
_returnUri = returnUri;
var authContext = new AuthenticationContext(_authority);
authContext.TokenCache.Clear();
}
public static void SetParameters(IPlatformParameters parameters)
{
_parameters = parameters;
}
public async Task<bool> LoginAsync()
{
_accessToken = await GetAccessTokenAsync();
return true;
}
public Task LogoutAsync()
{
var authContext = new AuthenticationContext(_authority);
var cachedToken = authContext.TokenCache.ReadItems().FirstOrDefault(t => t.Authority == _authority && t.ClientId == _clientId && t.Resource == _resource);
if (cachedToken != null)
{
authContext.TokenCache.DeleteItem(cachedToken);
}
UserInfo = null;
_accessToken = null;
return Task.CompletedTask;
}
private async Task<string> GetAccessTokenAsync()
{
var uri = new Uri(_returnUri);
var authContext = new AuthenticationContext(_authority);
var authResult = await authContext.AcquireTokenAsync(_resource, _clientId, uri, _parameters);
UserInfo = authResult.UserInfo;
return authResult.AccessToken;
}
}
Then in platform specific, call SetParameters method:
Example in Android on your MainActivity.cs :
protected override void OnCreate(Bundle bundle)
{
var platformParameters = new PlatformParameters(this);
AuthenticationManager.SetParameters(platformParameters);
}
Similarly on iOS:
public override void ViewDidLoad()
{
var platformParameters = new PlatformParameters(this);
AuthenticationManager.SetParameters(platformParameters);
}
You need to cast it to Activity
new PlatformParameters((Activity) PromptBehavior.Auto));
Related
I've just implemented some localStorage functions in a class to a song lyrics app I'm developing and I think I'm calling it wrong in some way but not sure how. Either that or something is blocking the async function from completing.
Storage Class
public class Sections
{
readonly string searchesFileName = "RecentSearches.txt";
readonly string songsFileName = "RecentSongs.txt";
readonly string playlistFileName = "Playlists.txt";
IFolder localFolder = FileSystem.Current.LocalStorage;
public async void VerifySectionFiles()
{
ExistenceCheckResult searchFileExists = await localFolder.CheckExistsAsync(searchesFileName);
if (searchFileExists != ExistenceCheckResult.FileExists)
{
await localFolder.CreateFileAsync(searchesFileName, CreationCollisionOption.FailIfExists);
}
ExistenceCheckResult songsFileExists = await localFolder.CheckExistsAsync(songsFileName);
if (songsFileExists != ExistenceCheckResult.FileExists)
{
await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.FailIfExists);
}
ExistenceCheckResult playlistFileExists = await localFolder.CheckExistsAsync(playlistFileName);
if (playlistFileExists != ExistenceCheckResult.FileExists)
{
await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.FailIfExists);
}
}
public async void AddRecentSong(string title, int id, string artist)
{
Song[] recentSongs = await ReadRecentSongsFromFile() ?? new Song[10];
recentSongs[9] = null;
for (int i = 9; i > 0; i--)
{
recentSongs[i] = recentSongs[i - 1];
}
recentSongs[0] = new Song(title, artist, id);
IFile songFile = await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.OpenIfExists);
string songsJsonString = JsonConvert.SerializeObject(recentSongs);
await songFile.WriteAllTextAsync(songsJsonString);
}
public async Task<Song[]> ReadRecentSongsFromFile()
{
IFile recentSongs = await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.OpenIfExists).ConfigureAwait(false);
string songsJsonString = await recentSongs.ReadAllTextAsync();
Song[] songsArray = JsonConvert.DeserializeObject<Song[]>(songsJsonString);
return songsArray;
}
public async void AddRecentSearch(string searchTerm)
{
string[] recentSearches = await ReadRecentSearchesFromFile() ?? new string[10];
recentSearches[9] = null;
for (int i = 9; i > 0; i--)
{
recentSearches[i] = recentSearches[i - 1];
}
recentSearches[0] = searchTerm;
IFile songFile = await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.OpenIfExists);
string songsJsonString = JsonConvert.SerializeObject(recentSearches);
await songFile.WriteAllTextAsync(songsJsonString);
}
public async Task<string[]> ReadRecentSearchesFromFile()
{
IFile recentSearches = await localFolder.CreateFileAsync(searchesFileName, CreationCollisionOption.OpenIfExists);
string searchesJsonString = await recentSearches.ReadAllTextAsync();
string[] searchesArray = JsonConvert.DeserializeObject<string[]>(searchesJsonString);
return searchesArray;
}
public async void CreatePlaylist(Playlist playlist)
{
List<Playlist> playlists = await ReadPlaylistsFromFile()?? new List<Playlist>();
playlists.Add(playlist);
IFile playlistsFile = await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.OpenIfExists);
string playlistsJsonString = JsonConvert.SerializeObject(playlists);
await playlistsFile.WriteAllTextAsync(playlistsJsonString);
}
public async void RemovePlaylist(Playlist playlist)
{
List<Playlist> playlists = await ReadPlaylistsFromFile() ?? new List<Playlist>();
Playlist playlistToRemove = playlists.Find(x => x == playlist);
playlists.Remove(playlistToRemove);
IFile playlistsFile = await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.OpenIfExists);
string playlistsJsonString = JsonConvert.SerializeObject(playlists);
await playlistsFile.WriteAllTextAsync(playlistsJsonString);
}
public async Task<List<Playlist>> ReadPlaylistsFromFile()
{
IFile playlists = await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.OpenIfExists);
string playlistsString = await playlists.ReadAllTextAsync();
List<Playlist> playlistList = JsonConvert.DeserializeObject<List<Playlist>>(playlistsString);
return playlistList;
}
}
And when it comes to the implementation, I've used both this by instantiating the class inside the page I'm using it:
public partial class MainPortrait : ContentView
{
Sections sections = new Sections();
public string[] RecentSearches = new string[10];
public string Search { get; set; }
public MainPortrait()
{
InitializeComponent();
BindingContext= this;
sections.VerifySectionFiles();
RecentSearches = sections.ReadRecentSearchesFromFile().Result;
//Do stuff with returned string[]
}
And this, where I added it to the App.xaml.cs
public partial class MainPortrait : ContentView
{
public string[] RecentSearches = new string[10];
public string Search { get; set; }
public MainPortrait()
{
InitializeComponent();
BindingContext= this;
((App)App.Current).sections.VerifySectionFiles();
RecentSearches = ((App)App.Current).sections.ReadRecentSearchesFromFile().Result;
//Do stuff with returned string[]
and it had the same problem both times, so I'm assuming it's a problem with the function itself. And if that's the case, there is probably a similar problem with all the functions in the Section class.
Any ideas?
i want to send commend in Firestore using a API .net core 3.1, and i am working using clean architecture. and an CQRS pattern.
like this:
namespace SmartRestaurant.Infrastructure.Services
{
public class FirebaseConfig
{
public string BasePath { get; set; }
}
public class FirebaseRepository : IFirebaseRepository
{
readonly string _DataBaseBasepath;
readonly FirestoreDb _db;
public FirebaseRepository(IOptions<FirebaseConfig> conf)
{
_DataBaseBasepath = conf.Value.BasePath;
if (string.IsNullOrEmpty(_DataBaseBasepath))
{
throw new ArgumentNullException("fireBase Path not found in appsettings");
}
var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
_db = FirestoreDb.Create("^projectName");
}
public async Task<T> AddAsync<T>(string path, T data, CancellationToken cancellationToken)
{
try
{
DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
var objectTosend = getOrderToDictionary(data);
await doc.SetAsync(objectTosend, null, cancellationToken);
return data;
}
catch (Exception exe)
{
throw exe;
}
}
public async Task<T> UpdateAsync<T>(string path, T data, CancellationToken cancellationToken)
{
try
{
DocumentReference doc = _db.Document(_DataBaseBasepath + "/" + path);
var objectTosend = getOrderToDictionary(data);
await doc.UpdateAsync(objectTosend, null, cancellationToken);
return data;
}
catch (Exception exe)
{
throw exe;
}
}
}
}
that code is in the infrastrecture layer and the interface of this service is in the application layer
public interface IFirebaseRepository
{
Task<T> AddAsync<T>(string path,T data,CancellationToken cancellationToken);
Task<T> UpdateAsync<T>(string path,T data, CancellationToken cancellationToken);
}
the injection of this service is in the infrastrecture layer like this
services.AddTransient < IFirebaseRepository, FirebaseRepository> ();
i use this service in the application layer like this :
public class OrdersCommandsHandlers : IRequestHandler<CreateOrderCommand, OrderDto>,
IRequestHandler<UpdateOrderCommand, NoContent>,
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;
private readonly IUserService _userService;
private readonly IFirebaseRepository _fireBase;
private readonly string CreateAction = "CreateAction";
private readonly string UpdateAction = "UpdateAction";
public OrdersCommandsHandlers(IApplicationDbContext context,
IMapper mapper,
IUserService userService,
IFirebaseRepository fireBase)
{
_context = context;
_mapper = mapper;
_userService = userService;
_fireBase = fireBase;
}
public async Task<OrderDto> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
{
var validator = new CreateOrderCommandValidator();
var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
if (!result.IsValid) throw new ValidationException(result);
....
await _context.SaveChangesAsync(cancellationToken);
var orderDto = _mapper.Map<OrderDto>(order);
orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
var path = request.FoodBusinessId + "/Orders/" + orderDto.OrderId;
await _fireBase.AddAsync(path, orderDto, cancellationToken);
......
return _mapper.Map<OrderDto>(newOrder);
}
public async Task<NoContent> Handle(UpdateOrderCommand request, CancellationToken cancellationToken)
{
var validator = new UpdateOrderCommandValidator();
var result = await validator.ValidateAsync(request, cancellationToken).ConfigureAwait(false);
if (!result.IsValid) throw new ValidationException(result);
......
var orderDto = _mapper.Map<OrderDto>(order);
var foodBusiness = await _context.FoodBusinesses.FindAsync(order.FoodBusinessId);
if (foodBusiness != null)
orderDto.CurrencyExchange = CurrencyConverter.GetDefaultCurrencyExchangeList(orderDto.TotalToPay, foodBusiness.DefaultCurrency);
var path = order.FoodBusinessId + "/Orders/" + order.OrderId;
await _fireBase.UpdateAsync(path,orderDto, cancellationToken);
return default;
}
}
when i deploiye this code in the server i have this erreur :
Error constructing handler for request of type MediatR.IRequestHandler`2[SmartRestaurant.Application.Orders.Commands.CreateOrderCommand,SmartRestaurant.Application.Common.Dtos.OrdersDtos.OrderDto]. Register your handlers with the container. See the samples in GitHub for examples.
i try to using fireStor in the NetCore 3 api with CQRS, and a erreur Appear when i use the hendler that contaie firebase service.
and locally all work well , the probleme appear in the server when i deploie the code.
the reason of this issues is in the constructor of firebaseService
public FirebaseRepository(IOptions<FirebaseConfig> conf)
{
_DataBaseBasepath = conf.Value.BasePath;
if (string.IsNullOrEmpty(_DataBaseBasepath))
{
throw new ArgumentNullException("fireBase Path not found in appsettings");
}
var pathConfigFile = Path.Combine(AppContext.BaseDirectory, "jsonProjectConfogFile.json");
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", pathConfigFile);
_db = FirestoreDb.Create("^projectName");
}
the conf.Value.BasePath is null in the server because the appsetting.json does not contain the FirebaseConfig section.
I'm struggling to make my asp.net core 2 app act like a reverse proxy using URL Rewrite rules.
I have the following in my startup.cs:
var rewriteRules = new RewriteOptions()
.AddRedirectToHttps();
.AddRewrite(#"^POC/(.*)", "http://192.168.7.73:3001/$1", true);
app.UseRewriter(rewriteRules);
The rewrite rule is exactly as it is in my IIS settings (which I'm trying to replace with this method) which works fine.
I'm assuming it has something to do with forwarding the headers maybe? Or maybe I just don't understand how the Rewrite Middleware is supposed to work, if you want the requests to be forwarded instead of just rewritten relative to current hostname.
A reverse proxy can be emulated/implemeted within a middleware :
First the startup class where we add a IUrlRewriter service and the ProxyMiddleware.
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUrlRewriter>(new SingleRegexRewriter(#"^/POC/(.*)", "http://192.168.7.73:3001/$1"));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseRewriter(new RewriteOptions().AddRedirectToHttps());
app.UseMiddleware<ProxyMiddleware>();
}
}
Next we will create a basic implementation of IUrlRewriter. The RewriteUri method must transform the HttpContext into an absolute Uri. Or null if the url should not be redirected in the middleware.
public interface IUrlRewriter
{
Task<Uri> RewriteUri(HttpContext context);
}
public class SingleRegexRewriter : IUrlRewriter
{
private readonly string _pattern;
private readonly string _replacement;
private readonly RegexOptions _options;
public SingleRegexRewriter(string pattern, string replacement)
: this(pattern, replacement, RegexOptions.None) { }
public SingleRegexRewriter(string pattern, string replacement, RegexOptions options)
{
_pattern = pattern ?? throw new ArgumentNullException(nameof(pattern));
_replacement = replacement ?? throw new ArgumentNullException(nameof(pattern));
_options = options;
}
public Task<Uri> RewriteUri(HttpContext context)
{
string url = context.Request.Path + context.Request.QueryString;
var newUri = Regex.Replace(url, _pattern, _replacement);
if (Uri.TryCreate(newUri, UriKind.Absolute, out var targetUri))
{
return Task.FromResult(targetUri);
}
return Task.FromResult((Uri)null);
}
}
And then the Middleware (stolen from an old verison of aspnet proxy repo) and customized. It get the IUrlRewrite service as parameter of Invoke method.
The pipeline is :
Try rewrite url
Create a HttpRequestMessage
Copy Request Header and content
Send the request
Copy response header
Copy response content
done
Et voila
public class ProxyMiddleware
{
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler()
{
AllowAutoRedirect = false,
MaxConnectionsPerServer = int.MaxValue,
UseCookies = false,
});
private const string CDN_HEADER_NAME = "Cache-Control";
private static readonly string[] NotForwardedHttpHeaders = new[] { "Connection", "Host" };
private readonly RequestDelegate _next;
private readonly ILogger<ProxyMiddleware> _logger;
public ProxyMiddleware(
RequestDelegate next,
ILogger<ProxyMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context, IUrlRewriter urlRewriter)
{
var targetUri = await urlRewriter.RewriteUri(context);
if (targetUri != null)
{
var requestMessage = GenerateProxifiedRequest(context, targetUri);
await SendAsync(context, requestMessage);
return;
}
await _next(context);
}
private async Task SendAsync(HttpContext context, HttpRequestMessage requestMessage)
{
using (var responseMessage = await _httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
if (!context.Response.Headers.ContainsKey(CDN_HEADER_NAME))
{
context.Response.Headers.Add(CDN_HEADER_NAME, "no-cache, no-store");
}
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
}
private static HttpRequestMessage GenerateProxifiedRequest(HttpContext context, Uri targetUri)
{
var requestMessage = new HttpRequestMessage();
CopyRequestContentAndHeaders(context, requestMessage);
requestMessage.RequestUri = targetUri;
requestMessage.Headers.Host = targetUri.Host;
requestMessage.Method = GetMethod(context.Request.Method);
return requestMessage;
}
private static void CopyRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
{
var requestMethod = context.Request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
}
foreach (var header in context.Request.Headers)
{
if (!NotForwardedHttpHeaders.Contains(header.Key))
{
if (header.Key != "User-Agent")
{
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
else
{
string userAgent = header.Value.Count > 0 ? (header.Value[0] + " " + context.TraceIdentifier) : string.Empty;
if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, userAgent) && requestMessage.Content != null)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, userAgent);
}
}
}
}
}
private static HttpMethod GetMethod(string method)
{
if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
if (HttpMethods.IsGet(method)) return HttpMethod.Get;
if (HttpMethods.IsHead(method)) return HttpMethod.Head;
if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
if (HttpMethods.IsPost(method)) return HttpMethod.Post;
if (HttpMethods.IsPut(method)) return HttpMethod.Put;
if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
return new HttpMethod(method);
}
}
Bonus : some other Rewriter
public class PrefixRewriter : IUrlRewriter
{
private readonly PathString _prefix;
private readonly string _newHost;
public PrefixRewriter(PathString prefix, string newHost)
{
_prefix = prefix;
_newHost = newHost;
}
public Task<Uri> RewriteUri(HttpContext context)
{
if (context.Request.Path.StartsWithSegments(_prefix))
{
var newUri = context.Request.Path.Value.Remove(0, _prefix.Value.Length) + context.Request.QueryString;
var targetUri = new Uri(_newHost + newUri);
return Task.FromResult(targetUri);
}
return Task.FromResult((Uri)null);
}
}
public class MergeRewriter : IUrlRewriter
{
private readonly List<IUrlRewriter> _rewriters = new List<IUrlRewriter>();
public MergeRewriter()
{
}
public MergeRewriter(IEnumerable<IUrlRewriter> rewriters)
{
if (rewriters == null) throw new ArgumentNullException(nameof(rewriters));
_rewriters.AddRange(rewriters);
}
public MergeRewriter Add(IUrlRewriter rewriter)
{
if (rewriter == null) throw new ArgumentNullException(nameof(rewriter));
_rewriters.Add(rewriter);
return this;
}
public async Task<Uri> RewriteUri(HttpContext context)
{
foreach (var rewriter in _rewriters)
{
var targetUri = await rewriter.RewriteUri(context);
if(targetUri != null)
{
return targetUri;
}
}
return null;
}
}
// In Statup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IUrlRewriter>(new MergeRewriter()
.Add(new PrefixRewriter("/POC/API", "http://localhost:1234"))
.Add(new SingleRegexRewriter(#"^/POC/(.*)", "http://192.168.7.73:3001/$1")));
}
Edit
I found a project to do same but with way more other feature https://github.com/damianh/ProxyKit as a nuget package
I'm using Xamarin, also my SQLite tables contain a large amount of data.
Because I want to avoid UIThread problems in OnCreate(), I need to perform database actions asynchronously.
I'm looking for guidance if I am handling this properly.
First method, which I found on the net:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.InventoryPreviewMain);
Thread thread = new Thread(() =>
{
SQLiteConnection db = new SQLiteConnection(dpPath);
var table = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where CategoryID =" + Connection.CategoryID + "");
mItems = new List<InventoryPreviewClass>();
foreach (var item in table)
{
mItems.Add(new InventoryPreviewClass() { InventoryItemID = item.InventoryItemID, InventoryItemName = item.InventoryItemName, InventoryItemPrice = item.InventoryItemPrice });
}
MyListViewAdapterInventory adapter = new MyListViewAdapterInventory(this, Resource.Layout.InventoryPreview, mItems);
mlistview.Adapter = adapter;
});
thread.Start();
Second Method, using async
public async void StartTimer()
{
SQLiteConnection db = new SQLiteConnection(dpPath);
var table = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where CategoryID =" + Connection.CategoryID + "");
mItems = new List<InventoryPreviewClass>();
foreach (var item in table)
{
mItems.Add(new InventoryPreviewClass() { InventoryItemID = item.InventoryItemID, InventoryItemName = item.InventoryItemName, InventoryItemPrice = item.InventoryItemPrice });
}
MyListViewAdapterInventory adapter = new MyListViewAdapterInventory(this, Resource.Layout.InventoryPreview, mItems);
mlistview.Adapter = adapter;
await Task.Delay(500);
}
Which of two examples are more safe for keeping alive UIthread? Is there any other solution for making this?What is more reccomended to do?
Answer
Use the Cross-platform SQLite Library made by #FrankKruger to create/access SQLite databases for Xamarin mobile apps.
This library has a built-in asynchronous connection, so you'll never need to worry about accessing the database from the UI Thread again!
Xamarin.Android Example
"Second Method"
public async Task StartTimer()
{
mItems = await InventoryPreviewClassDatabase.GetAllInventoryPreviewClassAsync();
MyListViewAdapterInventory adapter = new MyListViewAdapterInventory(this, Resource.Layout.InventoryPreview, mItems);
mlistview.Adapter = adapter;
}
BaseDatabase Class
using System;
using System.Threading.Tasks;
using SQLite;
namespace SampleApp
{
public abstract class BaseDatabase
{
#region Constant Fields
static readonly Lazy<SQLiteAsyncConnection> _databaseConnectionHolder = new Lazy<SQLiteAsyncConnection>(() => GetDatabaseConnection());
#endregion
#region Fields
static bool _isInitialized;
#endregion
#region Properties
static SQLiteAsyncConnection DatabaseConnection => _databaseConnectionHolder.Value;
#endregion
#region Methods
protected static async Task<SQLiteAsyncConnection> GetDatabaseConnectionAsync()
{
if (!_isInitialized)
await Initialize().ConfigureAwait(false);
return DatabaseConnection;
}
static async Task Initialize()
{
await DatabaseConnection.CreateTableAsync<InventoryPreviewClass>().ConfigureAwait(false);
_isInitialized = true;
}
SQLiteAsyncConnection GetDatabaseConnection()
{
var sqliteFilename = "YourDatabaseName.db3";
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); // Documents folder
var path = Path.Combine(documentsPath, sqliteFilename);
var conn = new SQLiteAsyncConnection(path, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);
return conn;
}
#endregion
}
}
Parent Database Class
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace SampleApp
{
public abstract class InventoryPreviewClassDatabase : BaseDatabase
{
#region Methods
public static async Task<IList<InventoryPreviewClass>> GetAllInventoryPreviewClassAsync()
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.Table<InventoryPreviewClass>().ToListAsync().ConfigureAwait(false);
}
public static async Task<InventoryPreviewClass> GetInventoryPreviewClassByIDAsync(int id)
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.Table<InventoryPreviewClass>().Where(x => x.ID.Equals(id)).FirstOrDefaultAsync().ConfigureAwait(false);
}
public static async Task<int> SaveInventoryPreviewClassAsync(InventoryPreviewClass inventoryPreview)
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
var isObjectInDatabase = await GetInventoryPreviewClassByIDAsync(inventoryPreview.ID).ConfigureAwait(false) != null;
if (isObjectInDatabase)
return await databaseConnection.UpdateAsync(inventoryPreview).ConfigureAwait(false);
return await databaseConnection.InsertAsync(inventoryPreview).ConfigureAwait(false);
}
public static async Task<int> DeleteItemAsync(OpportunityModel opportunity)
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.DeleteAsync(opportunity).ConfigureAwait(false);
}
public static async Task<int> GetNumberOfRowsAsync()
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.Table<InventoryPreviewClass>().CountAsync().ConfigureAwait(false);
}
#endregion
}
}
This code was inspired from this Xamarin.Forms sample app
Following Taiseer Joudeh excellent article
Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin (http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/) currently I am creating a Token based authentication with refresh token option.
My Startup class code is as follows:
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new SimpleAuthorizationServerProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
My SimpleAuthorizationServerProvider class code is as follows:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
//var id = "";
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
//Here set User.Identity.Id = RavenUserId, So rest of the user will be able to get it
//id = (user == null ? "0" : user.RavenUserId.ToString());
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
//So when we will call User.Identity.Id we will be able to get Raven User Id
// identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
if (newClaim != null)
{
newIdentity.RemoveClaim(newClaim);
}
newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
}
My SimpleRefreshTokenProvider class code is as follows:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using (var _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
using (var _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshToken(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
The AuthRepository class code is as follows:
public class AuthRepository : IDisposable
{
private AuthContext _ctx;
private UserManager<IdentityUser> _userManager;
public AuthRepository()
{
_ctx = new AuthContext();
_userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx));
}
public async Task<IdentityResult> RegisterUser(UserModel userModel)
{
IdentityUser user = new IdentityUser
{
UserName = userModel.UserName
};
var result = await _userManager.CreateAsync(user, userModel.Password);
return result;
}
public async Task<IdentityUser> FindUser(string userName, string password)
{
IdentityUser user = await _userManager.FindAsync(userName, password);
return user;
}
public Client FindClient(string clientId)
{
var client = _ctx.Clients.Find(clientId);
//var clients = _ctx.Clients;
//var client = _ctx.Clients.FirstOrDefault(x => x.Id==clientId);
return client;
}
public async Task<bool> AddRefreshToken(RefreshToken token)
{
var existingToken = _ctx.RefreshTokens.Where(r => r.Subject == token.Subject && r.ClientId == token.ClientId).SingleOrDefault();
if (existingToken != null)
{
var result = await RemoveRefreshToken(existingToken);
}
_ctx.RefreshTokens.Add(token);
return await _ctx.SaveChangesAsync() > 0;
}
public async Task<bool> RemoveRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
if (refreshToken != null)
{
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
return false;
}
public async Task<bool> RemoveRefreshToken(RefreshToken refreshToken)
{
_ctx.RefreshTokens.Remove(refreshToken);
return await _ctx.SaveChangesAsync() > 0;
}
public async Task<RefreshToken> FindRefreshToken(string refreshTokenId)
{
var refreshToken = await _ctx.RefreshTokens.FindAsync(refreshTokenId);
return refreshToken;
}
public List<RefreshToken> GetAllRefreshTokens()
{
return _ctx.RefreshTokens.ToList();
}
public void Dispose()
{
_ctx.Dispose();
_userManager.Dispose();
}
}
And the ajax code is:
$("#refresh").click(function () {
var token = sessionStorage.getItem(tokenKey);
var refresh = sessionStorage.getItem('isRefreshToken');
var refreshToken = sessionStorage.getItem('refreshToken');
if (refresh) {
var refreshdata = "grant_type=refresh_token&refresh_token=" + refreshToken + "&client_id=TokenBasedAuthentication";
console.log(refreshdata);
sessionStorage.setItem(tokenKey, '');
sessionStorage.setItem(isRefreshToken, '');
sessionStorage.setItem(refreshToken, '');
$.ajax({
url: '/token',
type: 'POST',
data: refreshdata,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
success: function (data) {
sessionStorage.setItem(tokenKey, data.access_token);
sessionStorage.setItem(isRefreshToken, true);
sessionStorage.setItem(refreshToken, data.refresh_token);
},
error: function (xhr) {
alert(xhr.status + ': ' + xhr.statusText);
}
});
}
});
Finally when I click on Refresh it returns me following error
error: "invalid_grant"
Last two days I tried to figure out but failed.
I had a problem where I was always receiving a invalid_grant error even though I knew the refresh_token was valid. Granted there are a lot of reasons why there might be a invalid_grant error, but after debugging through the code I discovered that my issue was in the CreateAsync method. The refreshTokenLifetime variable was null. Thus, when the RefreshToken is created, the ExpiresUtc value is already expired, causing the invalid_grant error. To resolve this I verified that I had a valid value for the refreshTokenLifetime variable.
var refreshTokenLifetime = context.OwinContext.Get<string>("as:RefreshTokenLifetime") ?? "60";
Try this.
Remove this line of code newIdentity.AddClaim(new Claim("newClaim", "newValue")); from your GrantRefreshToken function of SimpleAuthorizationServerProvider class. As this line is of no use.
It is duplicating the claim when you request for new refresh token. So it is opposing you.
In ReceiveAsync method in SimpleRefreshTokenProvider class:
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
inspect refreshToken object to make sure all of its attributes have valid values.
In my case ProtectedTicket property has invalid value (Date value instead of string) and it causes this error.