I'm new with Xamarin.Forms, and I'm finding a way to store data in App like: token, username. Now, I'm trying to use SecureStorgae, but the function getValue doesn't work, and I have no idea why.
My code:
public async Task CheckLogin(string usernameString, string passwordString)
{
using (var client = new HttpClient())
{
string url = "myUrl";
var json = JsonConvert.SerializeObject(new { username = usernameString, password=passwordString });
HttpContent content = new StringContent(json,Encoding.UTF8, "application/json");
var response = await client.PostAsync(url,content);
if (response.IsSuccessStatusCode)
{
var rs = JsonConvert.DeserializeObject<LoginResult>(response.Content.ReadAsStringAsync().Result);
var token = rs.result.token;
CrossSecureStorage.Current.SetValue("SessionToken",token);
CrossSecureStorage.Current.SetValue("Username", rs.result.userName);
var token1 = CrossSecureStorage.Current.GetValue("SessionToken");
await Navigation.PushAsync(new Home());
}
}
}
When my app is running, I can't get the value of SessionToken.
in iOS
Open the Entitlements.plist file and make sure that "Enable Keychain Access Groups" is checked. Also ensure that in Project->Options->iOS Bundle Signing, the Entitlements.plist is selected in Custom Entitlements for iPhoneSimulator platform.
in Android
It is required that the password is set by the application prior to use.
SecureStorageImplementation.StoragePassword = "Your Password";
For more detail you can access here
Related
I am implementing a custom token endpoint for my identityserver4 project. The goal is to issue a token based on validation of a more complex credentials model (a separate user database than Identity Server's built in "client/scope" concept) and issue a Jwt token with extra claims added to help with user identity and access rights in my custom api.
My code is something like this:
[HttpPost]
public IActionResult GetCustomApiToken(CustomUserCredentialsModel credentials)
{
var customUser = GetCustomValidatedUser(credentials); //validate user from DB
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(ApplicationSettings.SigningKey); // <--- DeveloperSigningCredential ???
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("user", customUser.ToString()) /* extra custom claims */ }),
Issuer = "my identity server",
Audience = "my custom api",
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return Ok(tokenHandler.WriteToken(token));
}
Mind you I have not tested the above completely yet, but something like that should work in Production provided the key is managed in ApplicationSettings.
But it will not work in development where the signing key is added through Identity Server 4's AddDeveloperSigningCredential() extension.
One solution is to add SigningCredentials in configuration for all Dev/Test environements (= hassle).
Can I resolve the signing credential at runtime (as they are set in Program/Startup) ?
(Also, yes I know: don't store the signing keys readable in appSettings, please disregard that for the above example.)
Ok, so I figured it out, you can inject the ISigningCredentialStore singleton and resolve the signingCredential from there:
private readonly ISigningCredentialStore _signingCredentialStore;
public CustomTokenController(ISigningCredentialStore signingCredentialStore)
{
_signingCredentialStore = signingCredentialStore ?? throw new ArgumentNullException(nameof(signingCredentialStore));
}
[HttpPost]
public async Task<IActionResult> GetCustomApiToken(CustomUserCredentialsModel credentials)
{
var userId = GetCustomValidatedUser(credentials);
if (userId == null) return Unauthorized();
var signingCredentials = await _signingCredentialStore.GetSigningCredentialsAsync();
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("userId", userId.ToString()) /* extra custom claims */ }),
Issuer = "my IdentityServer",
IssuedAt = DateTime.UtcNow,
Audience = "my api",
Expires = DateTime.UtcNow.AddDays(1),
SigningCredentials = signingCredentials
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return Ok(tokenHandler.WriteToken(token));
}
This worked for me and the Jwt token generated can be validated just like any token issued by the built in "connect/token" endpoint.
I have a xamarin app --> azureFunction --->BlobStorage. so far so good.
The AzureFunction is set with AuthorizationLevel.Function.
I have set the azure function Managed identity "ON"
I have assigned a role to the BlobStorage (Blob data Contributor)
I can successfully call the function using postman using the function key.
I would like to store the functionKey in the KeyVault and call it from my mobile app
Question
As anybody got a walkthrough and snippet how to integrate the keyvault with a function key and call it from a mobile app (xamarin forms) c#?
I do not want to hardcode any keys in my mobile app.
I would be very grateful.Lots of googling and nothing.
thanks
Suppose your requirement is call the function from the code. Maybe you could refer to the below code.
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
KeyVaultClient keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync("your Secret Identifier")
.ConfigureAwait(false);
string functionkey = secret.Value;
string functionhost = "https://your function.azurewebsites.net/api/function name";
var param = new Dictionary<string, string>() { { "code", functionkey } ,{ "name","george"} };
Uri functionurl = new Uri(QueryHelpers.AddQueryString(functionhost, param));
var request = (HttpWebRequest)WebRequest.Create(functionurl);
var response = (HttpWebResponse)request.GetResponse();
string responseString;
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
responseString = reader.ReadToEnd();
Console.WriteLine(responseString);
}
}
I am trying to write a .netcore API which gets a bearer token from third party Webapp. This .netcore API should access the Microsoft graph API and get the user group information back from Azure AD.
I was following the sample project https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore.
But unfortunately this uses AAD graph rather tha Microsoft graph API.
I tried to implement Graph API in the .netcore api project in the above sample.
Things I have tried
I have changed the AAD graph to Graph API in the AzureAdAuthenticationBuilderExtensions.cs(in the web app project)
options.Resource = "https://graph.microsoft.com";
Also I used the Microsoft.Graph nuget in the API project. And I am trying to create the GraphServiceClient using the code below
public GraphServiceClient GetClient(string accessToken, IHttpProvider provider = null)
{
var words = accessToken.Split(' ');
var token = words[1];
var delegateAuthProvider = new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
return Task.FromResult(0);
});
var graphClient = new GraphServiceClient(delegateAuthProvider, provider ?? new HttpProvider());
return graphClient;
}
And finally I am trying to access the user information using the code below,
public async Task<IEnumerable<Group>> GetGroupAsync(string accessToken)
{
var graphClient = GetClient(accessToken);
try
{
User me = await graphClient.Me.Request().GetAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
var user= await graphClient.Users["***"].Request().Expand("MemberOf").GetAsync();
var userEmail = "testemail#test.com";
var usergroup = await graphClient.Users[userEmail].GetMemberGroups(false).Request().PostAsync();
var groupList = new List<Group>();
foreach (var g in usergroup.CurrentPage)
{
var groupObject = await graphClient.Groups[g].Request().GetAsync();
groupList.Add(groupObject);
}
return groupList;
}
But when I try the code I am getting the error "Microsoft.Graph.ServiceException: Code: InvalidAuthenticationToken
Message: Access token validation failure.Inner error at Microsoft.Graph.HttpProvider."
Can somebody help me please?
Thanks in advance
The access token passed to GetGroupAsync is not correct , and i am confused why you need to split the token :
var words = accessToken.Split(' ');
var token = words[1];
But never mind , since you have modified options.Resource = "https://graph.microsoft.com"; ADAL will help you get access token for Microsoft Graph API in OnAuthorizationCodeReceived function , and save the tokens to cache .
To get the access token , you could use ADAL to get the token from cache :
AuthenticationResult result = null;
// Because we signed-in already in the WebApp, the userObjectId is know
string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
// Using ADAL.Net, get a bearer token to access the TodoListService
AuthenticationContext authContext = new AuthenticationContext(AzureAdOptions.Settings.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
ClientCredential credential = new ClientCredential(AzureAdOptions.Settings.ClientId, AzureAdOptions.Settings.ClientSecret);
result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
Then you could pass that token to your function:
await GetGroupAsync(result.AccessToken);
Modify your GetClient function to delete the split part:
public GraphServiceClient GetClient(string accessToken, IHttpProvider provider = null)
{
var delegateAuthProvider = new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.FromResult(0);
});
var graphClient = new GraphServiceClient(delegateAuthProvider, provider ?? new HttpProvider());
return graphClient;
}
I am lost after reading to many posts on the web, and need some advice.
I use ADAL 3.17.1 in my Xamarin.Forms project. Now with ADAL3, the refresh token and the AcquireTokenByRefreshTokenAsync are no longer available and are internally handled. But this Refresh token in stores in memory only and when IOS app goes in the background, or when the application is close and reopened, the user needs to log again.
Is is possible to let the user log once in the morning and keep the token valid for 8-10 hours?. And not asking to log in when the app start or resume in the next 8-10 hours? I can't find post on that. All posts are with use of Refresh token...
Here is the code in my Authenticator class that run in IOS:
public class Authenticator_iOS : IAuthenticator
{
public async Task<MultipleAuthResult> Authenticate(string authority, string resource, string resource2, string clientId, string returnUri)
{
MultipleAuthResult multipleAuth = new MultipleAuthResult();
var authContext = new AuthenticationContext(authority, new CustomTokenCache());
if (authContext.TokenCache.ReadItems().Any())
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
var controller = UIApplication.SharedApplication.KeyWindow.RootViewController;
var uri = new Uri(returnUri);
var platformParams = new PlatformParameters(controller);
platformParams.PromptBehavior = PromptBehavior.Auto;
try
{
multipleAuth.ResultBackEnd = await authContext.AcquireTokenAsync(resource, clientId, uri, platformParams); // Token for backend
multipleAuth.ResultGraph = await authContext.AcquireTokenAsync(resource2, clientId, uri, platformParams); // Token for Graph query
}
catch (Exception e)
{
return null;
}
return multipleAuth;
}
public void SingOut(string authority)
{
//Token
var authContext = new AuthenticationContext(authority);
if (authContext.TokenCache.ReadItems().Any())
{
authContext.TokenCache.Clear();
}
//Webview cookie
NSHttpCookieStorage CookieStorage = NSHttpCookieStorage.SharedStorage;
foreach (var cookie in CookieStorage.Cookies)
{
CookieStorage.DeleteCookie(cookie);
}
}
}
Looks like you're initializing a new cache instance every time your method fires.
var authContext = new AuthenticationContext(authority, new CustomTokenCache());
which renders this check moot:
if (authContext.TokenCache.ReadItems().Any())
Just remove initializing CustomTokenCache altogether, i have a feeling it will persist by default.
Do this instead:
var authContext = new AuthenticationContext(commonAuthority);
if (authContext.TokenCache.ReadItems().Count() > 0)
{
authContext = new AuthenticationContext(
authContext.TokenCache.ReadItems().First().Authority);
}
I am using ASP.NET Web API and Google.Apis.Drive.v2 Client Library for .NET to upload files to users Drive.
All examples of using the Drive Client Library for .NET require a authentication flow. But how should I create the DriveService when I already know the access token?
Despite the fact that have been 2 years since the question has been asked, today I've encountered the same situation and my solution is:
var valid_token = "Pass_the_valid_token_here";
var token = new Google.Apis.Auth.OAuth2.Responses.TokenResponse()
{
AccessToken = valid_token,
ExpiresInSeconds = 3600,
Issued = DateTime.Now
};
var fakeflow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "fakeClientId",
ClientSecret = "fakeClientSecret"
}
});
UserCredential credential = new UserCredential(fakeflow, "fakeUserId", token);
var serviceInitializer = new BaseClientService.Initializer()
{
//ApplicationName = "Storage Sample",
HttpClientInitializer = credential
};
DriveService service = new DriveService(serviceInitializer);
Update
You could create your own custom token but the issue with this is going to be that the client library will not be able to refresh your access without the refresh token.
var token = new Google.Apis.Auth.OAuth2.Responses.TokenResponse()
{
AccessToken = valid_token,
ExpiresInSeconds = 3600,
Issued = DateTime.Now
};
var authorization = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "lientId",
ClientSecret = "ClientSecret"
}
});
var credential = new UserCredential(authorization, "user", token);
The issue you are going to have with this is that the client library is not going to be able refersh the access token after it has expired since you are not supplying a refresh token its only going to work for an hour.
The answer from Svetoslav Georgiev has so far worked well for me - Can't thank you enough. Google really don't help themselves with the lack of .Net (Asp Core) samples etc. Anway, one problem I did run into was that of referer restriction, so a addition/slight modification to the answer - Once you have the "service" and want to say upload a file, you need to set the referer on a buried HttpClient property...
FilesResource.CreateMediaUpload uploadRequest;
byte[] byteArray = Encoding.UTF8.GetBytes(html);
using (var stream = new MemoryStream(byteArray))
{
uploadRequest = service.Files.Create(fileMetadata, stream, "text/html");
uploadRequest.Service.HttpClient.DefaultRequestHeaders.Referrer = new Uri($"{baseUrl}");
uploadRequest.Fields = "id";
var progress = uploadRequest.Upload();
if (progress.Exception != null)
{
throw progress.Exception;
}
var file = uploadRequest.ResponseBody;
.... do what you will with file ....
}