I have the class below that will be used in a xamarin.forms mobile application
to retrieve the token generated by OAuth(webapi). Once this is generated I need to store
in a place where I can access it again and not generating this all the time.
Where is the best place to store this in the Pcl? I will also want to be able to remove this once
the user logs off.
class LoginService
{
public async Task Login(string username, string password)
{
HttpWebRequest request = new HttpWebRequest(new Uri(String.Format("{0}Token", Constants.BaseAddress)));
request.Method = "POST";
string postString = String.Format("username={0}&password={1}&grant_type=password",
HttpUtility.HtmlEncode(username), HttpUtility.HtmlEncode(password));
byte[] bytes = Encoding.UTF8.GetBytes(postString);
using (Stream requestStream = await request.GetRequestStreamAsync())
{
requestStream.Write(bytes, 0, bytes.Length);
}
try
{
HttpWebResponse httpResponse = (HttpWebResponse)(await request.GetResponseAsync());
string json;
using (Stream responseStream = httpResponse.GetResponseStream())
{
json = new StreamReader(responseStream).ReadToEnd();
}
TokenResponseModel tokenResponse = JsonConvert.DeserializeObject(json);
return tokenResponse.AccessToken;
}
catch (Exception ex)
{
throw new SecurityException("Bad credentials", ex);
}
}
}
Token(s) being sensitive information, I would recommend storing them in a secure manner. Secure storage is available through Keychain services in iOS, and the KeyStore class in Android. Xamarin has a very good article on how to do that using Xamarin.Auth.
Other options available are:
BlobCache.Secure in Akavache
SecureStorage
Secure storage in XLabs
Just an update for anyone searching, as things have changed since this post was created. It is not advised to use the following any more:
Application.Current.Properties
To securely store things like access tokens etc you can use the Xamarin.Essentials SecureStorage static class.
Just add the Xamarin.Essentials nuget package if you don't already have it and use it like so:
using Xamarin.Essentials;
.
.
.
await SecureStorage.SetAsync("someKey", "someValue");
var myValue = await SecureStorage.GetAsync("someKey");
you also have the option to
SecureStorage.Remove("someKey");
//or
SecureStorage.RemoveAll();
Refer this for more documentation
Forms has a built in Properties dictionary where you can store small bits of persistent data.
Application.Current.Properties ["token"] = myToken;
Related
Question
I have an identity server implementation that is being used by a number of applications in test and production. I am currently working on a new feature, where the client application using the identity server can perform Azure service management REST api calls. For this, it needs a token. I can generate this token, store it and even access it in the AccountController in the identity server.
My issue is figuring out how to send this to the client. I don't think this token belongs in the claims for the user. So I tried to add it as part of AuthenticationProperties as a token, but I cannot seem to access it in the client. Should I store it in a session like this SO user did link? There is one answer to this question, but that does not seem right (I even tried it out of desperation!)
Relevant sections of code
Generate the token
var resource = "https://management.azure.com/";
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async context =>
{
// Acquire the token for the resource and save it
}
}
}
Restore it in AccountController
public async Task<IActionResult> ExternalLoginCallback(string returnUrl)
{
string resource = "https://management.azure.com/";
// snip
result = await authContext.AcquireTokenSilentAsync(resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// snip
AuthenticationProperties props = null;
var tokens = new List<AuthenticationToken>();
var id_token = info.Properties.GetTokenValue("id_token");
if (id_token != null)
{
tokens.Add(new AuthenticationToken { Name = "id_token", Value = id_token });
}
if (result != null)
{
tokens.Add(new AuthenticationToken { Name = "management_token", Value = result.AccessToken });
}
if (tokens.Any())
{
props = new AuthenticationProperties();
props.StoreTokens(tokens);
}
// snip
// Can I access these "props" on the client? I even tried adding it to `Items`, no luck.
await HttpContext.Authentication.SignInAsync(user.UserId, user.DisplayName, provider, props, additionalClaims.ToArray());
}
So, my question, is this the right way go about it? If so, how do I access the authentication properties set? Or should I try saving this in the Session? If so, how do I store it in the client's session?
Any pointers would help. Thank you!
Just wanted to post an answer so that people wanting the same can benefit.
A token cache can be implemented to achieve this. This repository explains how.
Pay special attention to the AdalDistributedTokenCache linked here
I am developing a multi-tenant application registered on my Azure AD that consumes Office 365 apis, Graph API etc.
I followed this Microsoft sample to build my work which uses ADAL .NET library and OpenIdConnect: Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.19.0.0
In ADAL.NET, we use an AuthenticationContext instance with a custom inherited class for the TokenCache (see code the sample code here).
For each request to the authorized resources, depending on the API, we invoke one of these methods (see code below) to get the auth_token that will be put in the request Bearer parameter. Is it the correct way to do it?
We never make use of the method AcquireTokenByRefreshTokenAsync, does it mean that our application never uses the refresh_token? Does it mean that our user will have to relog after one hour? Should we implement a kind of refreshing procedure with AcquireTokenByRefreshTokenAsync in the catch statement? Can it be made without prompting anything to the end-user?
REMARK: I posted a question regarding OpenIdConnect authentication ticket lifetime. To me these two questions are unrelated but they may be.
string signInUserId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
string tenantId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
public async Task<string> AcquireOutlook365TokenAsync()
{
AuthenticationContext authContext = new AuthenticationContext(string.Format("{0}/{1}", SettingsHelper.AuthorizationUri, tenantId), new ADALTokenCache(signInUserId));
try
{
var result = await authContext.AcquireTokenSilentAsync(#"https://outlook.office365.com/",
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
catch (AdalException exception)
{
//handle token acquisition failure
if (exception.ErrorCode == AdalError.FailedToAcquireTokenSilently)
{
authContext.TokenCache.Clear();
}
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
}
public async Task<string> AcquireAzureGraphTokenAsync()
{
AuthenticationContext authContext = new AuthenticationContext(string.Format("{0}/{1}", SettingsHelper.AuthorizationUri, tenantId), new ADALTokenCache(signInUserId));
try
{
var result = await authContext.AcquireTokenSilentAsync(#"https://graph.windows.net/",
new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey),
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
catch (AdalException exception)
{
//Same as other method
}
}
ADAL uses the stored refresh tokens automatically and transparently, you aren't required to perform any explicit action. AcquireTOkenByRefreshToken is in the ADAL surface for legacy reasons, and has been removed from version 3.x. More background at http://www.cloudidentity.com/blog/2015/08/13/adal-3-didnt-return-refresh-tokens-for-5-months-and-nobody-noticed/
I'm just a beginner on the .NET world and I've created a web api (.NET 4.5.2) and I'm using the annotation [Authorize] above my controllers like shown below:
[Authorize]
public class PhasesController : ApiController
{
private TestReportEntities db = new TestReportEntities();
// GET: api/Phases
public IQueryable<Phase> GetPhase()
{
return db.Phase;
}
}
I've already created my DB and I'm using the default tables that the web.api uses to manage the access, as you can see on this image:
My tables
I've already done a method to request to my web api, in another project/solution, it's working fine when I remove the annotation [Authorize] from my web api controllers.
this is an example about how I'm requesting my api:
public int GetCurrentIdPhase(int idProject)
{
int phaseId = -1;
WebRequest request = WebRequest.Create(string.Concat(URL, string.Format("api/phases/?idProject={0}", idProject)));
using (var resp = (HttpWebResponse)request.GetResponse())
{
using (var reader = new StreamReader(resp.GetResponseStream()))
{
string objText = reader.ReadToEnd();
var phase = JsonConvert.DeserializeObject<List<Phase>>(objText);
phaseId = phase[0].id;
}
}
if (phaseId != -1)
{
return phaseId;
}
else
{
throw new Exception("Phase not found");
}
}
At the end of the day my questions are:
How can I request a token to my api (POST - www.myApi/token) using the example above?
How can I use the token, once I've got it, on every request to my API?
if you can help me I would really appreciate it.
Thanks.
I've created a method to get the Token from my Web API, this is the method:
var request = (HttpWebRequest)WebRequest.Create(string.Concat(URL, "token"));
var postData = "grant_type=password";
postData += string.Format("&userName={0}", user);
postData += string.Format("&password={0}", pass);
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
string objText = new StreamReader(response.GetResponseStream()).ReadToEnd();
var requestedToken = (JObject)JsonConvert.DeserializeObject(objText);
token = string.Concat(token, requestedToken["access_token"].Value<string>());
And to request something to my API all I need to do is just add the token on the header of all requests like shown on the line below:
request.Headers.Add(HttpRequestHeader.Authorization, getToke());
Hope it can help someone else who is beginning to work with .NET web API like me.
Regards.
Im assuming the "GetCurrentIdPhase" call is from an unrelated app with unrealted auth - if any auth.
The difficulty here is in using Authorize and the traidtional browser authentication flow. Here's an example of changing the pipeline a bit to use a different auth form for using console/desktop apps. You don't say where you are calling GetCurrentIdPhase from so I'll have to assume either a separate app. If its a web app and you are authenticated using the same tables, then you will have to share the token between them using for ex. the url blackice provided above.
If the app is a desktop/console/etc (not another app that the user had to auth against the same tables) then you can try this approach to change how auth is done to make it easier to access.
MVC WebAPI authentication from Windows Forms
Currently, I use custom written authentication code for my site, which is built on .NET. I didn't take the standard Forms Auth route, as all the examples I could find were tightly integrated with WebForms, which I do not use. For all intents and purposes, I have all static HTML, and any logic is done through Javascript and web service calls. Things like logging in, logging off, and creating a new account are done without even leaving the page.
Here's how it works now: In the database, I have a User ID, a Security ID, and a Session ID. All three are UUIDs, and the first two never change. Each time the user logs on, I check the user table for a row that matches that username and hashed password, and I update the Session ID to a new UUID. I then create a cookie that's a serialized representation of all three UUIDs. In any secure web service calls, I deserialize that cookie that make sure there's a row in the users table with those 3 UUIDs. It's a fairly simple system and works well, however I don't really like the fact that a user can only be logged on with one client at a time. It's going to cause issues when I create mobile and tablet apps, and already creates issues if they have multiple computers or web browsers. For this reason, I'm thinking about throwing away this system and going with something new. Since I wrote it years ago, I figure there might be something much more recommended.
I've been reading up on the FormsAuthentication class in the .NET Framework, which handles auth cookies, and runs as an HttpModule to validate each request. I'm wondering if I can take advantage of this in my new design.
It looks like cookies are stateless, and sessions don't have to be tracked within the database. This is done through the fact that cookies are encrypted with a private key on the server, that can also be shared across a cluster of web servers. If I do something like:
FormsAuthentication.SetAuthCookie("Bob", true);
Then in later requests, I can be assured that Bob is indeed a valid user as a cookie would be very difficult if not impossible to forge.
Would I be wise to use the FormsAuthentication class to replace my current authentication model with? Rather than have a Session ID column in the database, I'd rely on encrypted cookies to represent valid sessions.
Are there third party/open source .NET authentication frameworks that might work better for my architecture?
Will this authentication mechanism cause any grief with code running on mobile and tablet clients, such as an iPhone app or Windows 8 Surface app? I would assume this would work, as long as these apps could handle cookies. Thanks!
Since I didn't get any responses, I decided to take a shot at this myself. First, I found an open source project that implements session cookies in an algorithm agnostic way. I used this as a starting point to implement a similar handler.
One issue I had with the built in ASP.NET implementation, which is a similar restriction in the AppHarbor implementation, is sessions are only keyed by a string username. I wanted to be able to store arbitrary data to identify a user, such as their UUID in the database as well as their logon name. As much of my existing code assumes this data is available in the cookie, it would take a lot of refactoring if this data were no longer available. Plus, I like the idea of being able to store basic user information without having to hit the database.
Another issue with the AppHarbor project, as pointed out in the this open issue, is the encryption algorithm isn't verified. This is not exactly true, as AppHarbor is algorithm agnostic, however it was requested that the sample project should show how to use PBKDF2. For that reason, I decided to use this algorithm (implemented in the .NET Framework through the Rfc2898DeriveBytes class) in my code.
Here's what I was able to come up with. It's meant as a starting point for someone looking to implement their own session management, so feel free to use it for whatever purpose you see fit.
using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Web;
namespace AuthTest
{
[Serializable]
public class AuthIdentity : IIdentity
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public AuthIdentity() { }
public AuthIdentity(Guid id, string name)
{
Id = id;
Name = name;
}
public string AuthenticationType
{
get { return "CookieAuth"; }
}
public bool IsAuthenticated
{
get { return Id != Guid.Empty; }
}
}
[Serializable]
public class AuthToken : IPrincipal
{
public IIdentity Identity { get; set; }
public bool IsInRole(string role)
{
return false;
}
}
public class AuthModule : IHttpModule
{
static string COOKIE_NAME = "AuthCookie";
//Note: Change these two keys to something else (VALIDATION_KEY is 72 bytes, ENCRYPTION_KEY is 64 bytes)
static string VALIDATION_KEY = #"MkMvk1JL/ghytaERtl6A25iTf/ABC2MgPsFlEbASJ5SX4DiqnDN3CjV7HXQI0GBOGyA8nHjSVaAJXNEqrKmOMg==";
static string ENCRYPTION_KEY = #"QQJYW8ditkzaUFppCJj+DcCTc/H9TpnSRQrLGBQkhy/jnYjqF8iR6do9NvI8PL8MmniFvdc21sTuKkw94jxID4cDYoqr7JDj";
static byte[] key;
static byte[] iv;
static byte[] valKey;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AuthenticateRequest += OnAuthenticateRequest;
context.EndRequest += OnEndRequest;
byte[] bytes = Convert.FromBase64String(ENCRYPTION_KEY); //72 bytes (8 for salt, 64 for key)
byte[] salt = bytes.Take(8).ToArray();
byte[] pw = bytes.Skip(8).ToArray();
Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pw, salt, 1000);
key = k1.GetBytes(16);
iv = k1.GetBytes(8);
valKey = Convert.FromBase64String(VALIDATION_KEY); //64 byte validation key to prevent tampering
}
public static void SetCookie(AuthIdentity token, bool rememberMe = false)
{
//Base64 encode token
var formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, token);
byte[] buffer = stream.GetBuffer();
byte[] encryptedBytes = EncryptCookie(buffer);
string str = Convert.ToBase64String(encryptedBytes);
var cookie = new HttpCookie(COOKIE_NAME, str);
cookie.HttpOnly = true;
if (rememberMe)
{
cookie.Expires = DateTime.Today.AddDays(100);
}
HttpContext.Current.Response.Cookies.Add(cookie);
}
public static void Logout()
{
HttpContext.Current.Response.Cookies.Remove(COOKIE_NAME);
HttpContext.Current.Response.Cookies.Add(new HttpCookie(COOKIE_NAME, "")
{
Expires = DateTime.Today.AddDays(-1)
});
}
private static byte[] EncryptCookie(byte[] rawBytes)
{
TripleDES des = TripleDES.Create();
des.Key = key;
des.IV = iv;
MemoryStream encryptionStream = new MemoryStream();
CryptoStream encrypt = new CryptoStream(encryptionStream, des.CreateEncryptor(), CryptoStreamMode.Write);
encrypt.Write(rawBytes, 0, rawBytes.Length);
encrypt.FlushFinalBlock();
encrypt.Close();
byte[] encBytes = encryptionStream.ToArray();
//Add validation hash (compute hash on unencrypted data)
HMACSHA256 hmac = new HMACSHA256(valKey);
byte[] hash = hmac.ComputeHash(rawBytes);
//Combine encrypted bytes and validation hash
byte[] ret = encBytes.Concat<byte>(hash).ToArray();
return ret;
}
private static byte[] DecryptCookie(byte[] encBytes)
{
TripleDES des = TripleDES.Create();
des.Key = key;
des.IV = iv;
HMACSHA256 hmac = new HMACSHA256(valKey);
int valSize = hmac.HashSize / 8;
int msgLength = encBytes.Length - valSize;
byte[] message = new byte[msgLength];
byte[] valBytes = new byte[valSize];
Buffer.BlockCopy(encBytes, 0, message, 0, msgLength);
Buffer.BlockCopy(encBytes, msgLength, valBytes, 0, valSize);
MemoryStream decryptionStreamBacking = new MemoryStream();
CryptoStream decrypt = new CryptoStream(decryptionStreamBacking, des.CreateDecryptor(), CryptoStreamMode.Write);
decrypt.Write(message, 0, msgLength);
decrypt.Flush();
byte[] decMessage = decryptionStreamBacking.ToArray();
//Verify key matches
byte[] hash = hmac.ComputeHash(decMessage);
if (valBytes.SequenceEqual(hash))
{
return decMessage;
}
throw new SecurityException("Auth Cookie appears to have been tampered with!");
}
private void OnAuthenticateRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
var cookie = context.Request.Cookies[COOKIE_NAME];
if (cookie != null && cookie.Value.Length > 0)
{
try
{
var formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
var bytes = Convert.FromBase64String(cookie.Value);
var decBytes = DecryptCookie(bytes);
stream.Write(decBytes, 0, decBytes.Length);
stream.Seek(0, SeekOrigin.Begin);
AuthIdentity auth = formatter.Deserialize(stream) as AuthIdentity;
AuthToken token = new AuthToken() { Identity = auth };
context.User = token;
//Renew the cookie for another 100 days (TODO: Should only renew if cookie was originally set to persist)
context.Response.Cookies[COOKIE_NAME].Value = cookie.Value;
context.Response.Cookies[COOKIE_NAME].Expires = DateTime.Today.AddDays(100);
}
catch { } //Ignore any errors with bad cookies
}
}
private void OnEndRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
var response = context.Response;
if (response.Cookies.Keys.Cast<string>().Contains(COOKIE_NAME))
{
response.Cache.SetCacheability(HttpCacheability.NoCache, "Set-Cookie");
}
}
}
}
Also, be sure to include the following module in your web.config file:
<httpModules>
<add name="AuthModule" type="AuthTest.AuthModule" />
</httpModules>
In your code, you can lookup the currently logged on user with:
var id = HttpContext.Current.User.Identity as AuthIdentity;
And set the auth cookie like so:
AuthIdentity token = new AuthIdentity(Guid.NewGuid(), "Mike");
AuthModule.SetCookie(token, false);
I don't want to do anything fancy on Twitter except post to it via my site once a day. I have searched around a bit and there are all sorts of super-complex ways to do every little thing that Twitter does, but there seems to be little documentation on how to do the simplest thing, which is make a post!
Does anyone know how to do this? Or can you at least point me in the right direction? I don't need full wrappers or anything (http://apiwiki.twitter.com/Libraries#C/NET), just one simple function that will post to Twitter.
Thanks!
This is the easiest implementation ever. Up and running in under 2 minutes: Twitterizer
Its fairly simple; you just need to post an xml file to a web page using webrequest.create. This example is close (assumes you have the xml for the message in another place and just pass it into twitterxml variable as a string. The url might not be the right one; found it on this [page][1] which defines the interface
WebRequest req = null;
WebResponse rsp = null;
try
{
string twitterXML = "xml as string";
string uri = "http://twitter.com/statuses/update.format";
req = WebRequest.Create(uri);
//req.Proxy = WebProxy.GetDefaultProxy(); // Enable if using proxy
req.Method = "POST"; // Post method
req.ContentType = "text/xml"; // content type
// Wrap the request stream with a text-based writer
StreamWriter writer = new StreamWriter(req.GetRequestStream());
// Write the XML text into the stream
writer.WriteLine(twitterXML);
writer.Close();
// Send the data to the webserver
rsp = req.GetResponse();
}
[1]: http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses update
There are a couple different ways of doing this, they vary depending on the tools you want to use and have access to. Option 1 will work right out of the box, but the coding can be complicated. Option 3 you will have to download tools for, but once there installed and loaded you should be able to consume the twitter api very quickly.
Use WebRequest.Create to create/send messages to remote endpoints
Use WCF, create a mirror endpoint and access the twitter api using client only endpoint.
Use the WCF REST Starter Kit Preview 2, which has a new class called the HttpClient. I would have to recommend this technique if you can. Here is a great video Consuming a REST Twitter Feed in under 3 minutes.
Here is a sample of using the WCF REST Starter Kit's HttpClient:
public void CreateFriendship(string friend)
{
using (var client = new HttpClient())
{
var url = string.Format("http://www.twitter.com/friendships/create/{0}.xml?follow=true", friend);
client.Post(url)
.CheckForTwitterError()
.EnsureStatusIs(HttpStatusCode.OK);
}
}
Add a comment if you'd like more info about a particular method.
Update:
For Option #1 see this question: Remote HTTP Post with C#
There are a few ways of doing this, you can check out http://restfor.me/twitter and it will give you the code from RESTful documentation.
Essentially making any authenticated call you can follow this logic:
///
/// Executes an HTTP POST command and retrives the information.
/// This function will automatically include a "source" parameter if the "Source" property is set.
///
/// The URL to perform the POST operation
/// The username to use with the request
/// The password to use with the request
/// The data to post
/// The response of the request, or null if we got 404 or nothing.
protected string ExecutePostCommand(string url, string userName, string password, string data) {
WebRequest request = WebRequest.Create(url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password)) {
request.Credentials = new NetworkCredential(userName, password);
byte[] bytes = Encoding.UTF8.GetBytes(data);
request.ContentLength = bytes.Length;
using (Stream requestStream = request.GetRequestStream()) {
requestStream.Write(bytes, 0, bytes.Length);
using (WebResponse response = request.GetResponse()) {
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
return reader.ReadToEnd();
}
}
}
}
return null;
}