Coming from C# and other languages, and new to F# and trying to port an SDK Library that I built in an OO language. The SDK is responsible for retrieving access token first, setting on a static field and then setting a specific interval to continuosly refresh the token before it's expiry.
The token is set as static field on the Authentication class and is updated every time before it reaches expiry.
Then other actors within the SDK reach out to Authentication class, read it's static field token, place in their Authorization headers before calling the REST endpoints. All actors within the SDK reuse the same token throughout for each call until it's expired and a newer one is auto fetched.
That's the behavior, I'm still trying to wrap my head around several concepts but I believe in learning while doing it.
This F# library will be called from C# where it will be passed Credentials to begin with and then subsequently instantiating other classes/actors and calling their methods while passing params for each individual method. Those other actors are the one who will be using this stored token.
The gist is basically having two static fields within the Authentication and enable access to other actors while refreshing the one of the static fields i.e. Token.
public class Authentication
{
public static Token token;
public static Credentials credentials;
public static Token RequestToken(Credentials credentials)
{
Authentication.credentials = credentials // cache for subsequent use
// http REST call to access token based on credentials/api keys etc.
// Authentication.token = someAccessTokenObject; // cache result
}
public static Token AddTokenObserver(Credentials credentials)
{
this.RequestToken(credentials);
// set interval, like call RequestToken every 24 hrs
}
}
public class Class1
{
public someReturnObject RequestService1(someParams) {
// accesses Authentication.credentials
// accesses Authentication.token
// places in the authorization headers
// and calls the web service
}
// + several other methods that repeats the same pattern
}
public class Class2
{
public someReturnObject RequestService2(someParams) {
// accesses Authentication.credentials
// accesses Authentication.token
// places in the authorization headers
// and calls the web service
}
// + several other methods that repeats the same pattern
}
Use of SDK
// initialize SDK by passing credentials and enable auto refresh
Authentication.AddTokenObserver(someCredentials) // set token observer
Class1 c1 = new Class1();
c1.RequestService1(someObject1); // uses credentials & token from Authentication
Class c2 = new Class2();
c2.RequestService2(someObject2); // uses credentials & token from Authentication
My F# Attempt
type Credentials = {mutable clientId: string; mutable clientSecret: string;}
type Token = {mutable access_token: string; mutable refresh_token: string}
type Authentication =
static member token = {access_token = ""; refresh_token = ""};
static member credentials = {clientId = ""; clientSecret = "";}
new() = {}
member this.RequestToken(credentials) =
let data : byte[] = System.Text.Encoding.ASCII.GetBytes("");
let host = "https://example.com";
let url = sprintf "%s&client_id=%s&client_secret=%s" host credentials.clientId credentials.clientSecret
let request = WebRequest.Create(url) :?> HttpWebRequest
request.Method <- "POST"
request.ContentType <- "application/x-www-form-urlencoded"
request.Accept <- "application/json;charset=UTF-8"
request.ContentLength <- (int64)data.Length
use requestStream = request.GetRequestStream()
requestStream.Write(data, 0, (data.Length))
requestStream.Flush()
requestStream.Close()
let response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
let output = reader.ReadToEnd()
printf "%A" response.StatusCode // if response.StatusCode = HttpStatusCode.OK throws an error
Authentication.credentials.clientId <- credentials.clientId
let t = JsonConvert.DeserializeObject<Token>(output)
Authentication.token.access_token <- t.access_token
Authentication.token.token_type <- t.token_type
reader.Close()
response.Close()
request.Abort()
F# Test
[<TestMethod>]
member this.TestCredentials() =
let credentials = {
clientId = "some client id";
clientSecret = "some client secret";
}
let authenticaiton = new Authentication()
try
authenticaiton.RequestToken(credentials)
printfn "%s, %s" credentials.clientId Authentication.credentials.clientId // Authentication.credentials.clientId is empty string
Assert.IsTrue(credentials.clientId = Authentication.credentials.clientId) // fails
with
| :? WebException -> printfn "error";
Question
In the above unit test
Authentication.credentials.clientId is empty string
Assert fails
I couldn't access static members within my unit tests after I called the token service. There's something wrong with how I'm approaching this all together.
I need help in translating C# behavior in F# with the help of some F# code. I've built the Authentication class and have some problems in the implementation especially around static members and subsequently accessing them. Also I want to follow the rules of functional programming and learn how it's done in Functional World in F#. Please help me translate this behavior in F# code.
The idiomatic functional approach to this problem, would be to first try to get rid of the global state.
There are several approaches to this, but the one that I think will work best would be to provide an AuthenticationContext which has the data your C# code keeps in global state, and make each call that migth renew the credentials, return its result together with a potentially updated authorization context.
Basically, given a method to make an API call with a token
type MakeApiCall<'Result> = Token -> 'Result
we want to create something like this:
type AuthenticatedCall<'Result> = AuthenticationContext -> 'Result * AuthenticationContext
You can then also let the context keep track of whether it needs renewal (e.g. by storing the timestamp when it was last renewed, storing the expiry date, or something else), and provide two functions
type NeedsRenewal = AuthenticationContext -> bool
type Renew = AuthenticationContext -> AuthenticationContext
Now, if you obtain the credentials with a function
type GetAccessToken = AuthenticationContext -> Token * AuthenticationContext
you can let the implementation of that method start by checking if the credentials need renewal, and if so renew them before returning.
So, a sample implementation might look like this:
type AuthenticationContext = {
credentials : Credentials
token : Token
expiryDate : DateTimeOffset
}
let needsRenewal context =
context.expiryDate > DateTimeOffset.UtcNow.AddMinutes(-5) // add some safety margin
let renew context =
let token = getNewToken context.Credentials
let expiryDate = DateTimeOffset.UtcNow.AddDays(1)
{ context with token = token, expiryDate = expiryDate }
let getAccessToken context =
let context' =
if needsRenewal context
then renew context
else context
return context'.token, context'
let makeAuthenticatedCall context makeApicall =
let token, context' = getAccessToken context
let result = makeApiCall token
result, context'
Now, if every time you make an API call you have access to the AuthenticationContext from the previous call, the infrastructure will take care of renewing the token for you.
You'll notice quickly that this will just push the problem to keeping track of the authentication context, and that you will have to pass that around a lot. For example, if you want to make two consecutive API calls, you'll do the following:
let context = getInitialContext ()
let resultA, context' = makeFirstCall context
let resultB, context'' = makeSecondCall context'
Wouldn't it be nice if we could build something that would keep track of the context for us, so we didn't have to pass it around?
It turns out there's a functional pattern for this situation.
Related
I have a WebApi that I want to authorize my user with his linkedin information (as in create an access token and inject it in to my owin).
So far I have tried to work with Sparkle.Linkedin and this is what I have
public LinkedInLogic() {
// create a configuration object
_config = new LinkedInApiConfiguration(ApiKey, ApiSecret);
// get the APIs client
_api = new LinkedInApi(_config);
}
public Uri GetAuthUrl() {
var scope = AuthorizationScope.ReadBasicProfile;
var state = Guid.NewGuid().ToString();
var redirectUrl = "http://localhost:1510/api/login/RedirectAuth";
return _api.OAuth2.GetAuthorizationUrl(scope, state, redirectUrl);
}
public void GetAccessToken(string code) {
//If I do api.GetAccessToken(code); here I get an access token
var request = System.Net.WebRequest.Create("http://localhost:1510/api/token?grant_type=authorization_code&code=" + code);
request.GetResponse(); // my owin authorization
}
So I first get the Authorization Url -> it opens a popup -> I enter my data and it goes back to a controller which fires up GetAccessToken.
Problem is even if I completely authorize with linkedin I am not sure how to authorize with my own webapi. So I tried to send an http request to my owin token giver but it doesn't like it. There is also doesn't seem to be anyway I can return the access token back to the user so he can use it in his session.
Any ideas?
Not too sure if the sparkle is working anymore since the changes that where made by Linkedin on May 2015
I'm looking for a way to use an existing session ID with the ServiceStack ServerEventsClient. I'd like to use server events, but access will need to be limited to authenticated users.
For JsonServiceClient, I have a method in a library referenced by all of our projects which returns an authenticated JsonServiceClient ready to be used. The purpose is to make development a bit faster by keeping all auth info in one place:
public static JsonServiceClient GetAuthenticatedServiceClient()
{
var ServiceClient = new JsonServiceClient(globals.ApiUrl)
{
RequestFilter = request => request.UserAgent = globals.ClientSoftware.ToString()
};
var CookieQuery = (from c in globals.AuthCookieContainer
where c.Name == "ss-id"
where c.Expires > DateTime.Now
select c);
if (CookieQuery.Count() > 0)
{
ServiceClient.CookieContainer.Add(CookieQuery.FirstOrDefault());
}
else
{
throw new Exceptions.ApiNotAuthenticatedException();
}
return ServiceClient;
}
My question is: Is there a way to implement something similar to the above method for ServerEventsClient? I'm trying to avoid sending an Authenticate request, since I've already got an easy way to get to session information on the client.
The ServerEventsClient.ServiceClient used to maintain the CookieContainer for each ServerEvents request is just a JsonServiceClient so you can access its concrete type with just:
var client = (JsonServiceClient)serverEventsClient.ServiceClient;
So you can take the same approach of transferring cookies between any service client.
The ServerEventsClient.Authenticate literally just makes an Authenticate request on the same ServerEventsClient.ServiceClient instance so it gets populated with the returned cookies from a successful response.
I was tasked with adding logging via external service (using SAML 2.0) to an MVC app (.Net 4.5) that uses SimpleMembership. To be honest I'm not even sure where to start. From what I found on the internet there are few points to the problem. Most of the materials I found dealt with communication with the SAML identity provider (frequently written from scratch). However before I can reach that point I need to make sure I can actually integrate it with the SimpleMembership which we are using.
I suspect for starters I would need something like SAMLWebSecurity (akin to OAuthWebSecurity which we also use). I have found no such thing* on the internet which makes me believe it does not exist (though I wouldn't mind being wrong here). This makes me believe I would have to write it myself, but can I do that without have to write my own membership provider?
*I'm not sure what would be a correct way to call this static class.
I'd recommend that you upgrade to ASP.NET Identity and the OWIN Based authentication middleware. Then you can use Kentor.AuthServices middleware that works with ASP.NET Identity (except that the XSRF-guard has to be commented out until bug #127 has been resolved).
You could also use the SAML classes from Kentor.AuthServices if you have to stick with SimpleMembership, so that you don't have to implement SAML from scratch.
Disclaimer: I'm the author of Kentor.AuthServices, but since it's open source, I'm not making money on people using it.
After discussing it with a colleague I think I figured out the course of actions. Both OAuthWebSecurity and WebSecurity appear to be a part of SimpleMembership, so what I wrote in the question would indicate I want to write a custom membership or reverse engineer SimpleMembership to copy OAuthWebSecurity (which doesn't sound like a fun activity to have).
My best bet here is hijacking the OAuthWebSecurity, by writing a custom client (one which implements the IAuthenticationClient interface). Normally one registers various OAuth clients using OAuthWebSecurity's built in methods (like RegisterFacebookClient). But it is also possible to register those clients using OAuthWebSecurity.RegisterClient which accepts IAuthenticationClient. This way I should be able to add this SAML login without writing a custom membership provider and keep using SimpleMembership.
I managed to do this. Thankfully the identity provider wasn't extremely complicated so all I had to do was redirect to a certain address (I didn't even need to request assertion). After a successful login, the IDP "redirects" the user using POST to my site with the base64 encoded SAMLResponse attached. So all I had to do was to parse and validate the response. I placed the code for this in my custom client (implementing IAuthenticationClient interface).
public class mySAMLClient : IAuthenticationClient
{
// I store the IDP certificate in App_Data
// This can by actually skipped. See VerifyAuthentication for more details
private static X509Certificate2 certificate = null;
private X509Certificate2 Certificate
{
get
{
if (certificate == null)
{
certificate = new X509Certificate2(Path.Combine(HttpContext.Current.ApplicationInstance.Server.MapPath("~/App_Data"), "idp.cer"));
}
return certificate;
}
}
private string providerName;
public string ProviderName
{
get
{
return providerName;
}
}
public mySAMLClient()
{
// This probably should be provided as a parameter for the constructor, but in my case this is enough
providerName = "mySAML";
}
public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
// Normally you would need to request assertion here, but in my case redirecting to certain address was enough
context.Response.Redirect("IDP login address");
}
public AuthenticationResult VerifyAuthentication(HttpContextBase context)
{
// For one reason or another I had to redirect my SAML callback (POST) to my OAUTH callback (GET)
// Since I needed to retain the POST data, I temporarily copied it to session
var response = context.Session["SAMLResponse"].ToString();
context.Session.Remove("SAMLResponse");
if (response == null)
{
throw new Exception("Missing SAML response!");
}
// Decode the response
response = Encoding.UTF8.GetString(Convert.FromBase64String(response));
// Parse the response
var assertion = new XmlDocument { PreserveWhitespace = true };
assertion.LoadXml(response);
//Validating signature based on: http://stackoverflow.com/a/6139044
// adding namespaces
var ns = new XmlNamespaceManager(assertion.NameTable);
ns.AddNamespace("samlp", #"urn:oasis:names:tc:SAML:2.0:protocol");
ns.AddNamespace("saml", #"urn:oasis:names:tc:SAML:2.0:assertion");
ns.AddNamespace("ds", #"http://www.w3.org/2000/09/xmldsig#");
// extracting necessary nodes
var responseNode = assertion.SelectSingleNode("/samlp:Response", ns);
var assertionNode = responseNode.SelectSingleNode("saml:Assertion", ns);
var signNode = responseNode.SelectSingleNode("ds:Signature", ns);
// loading the signature node
var signedXml = new SignedXml(assertion.DocumentElement);
signedXml.LoadXml(signNode as XmlElement);
// You can extract the certificate from the response, but then you would have to check if the issuer is correct
// Here we only check if the signature is valid. Since I have a copy of the certificate, I know who the issuer is
// So if the signature is valid I then it was sent from the right place (probably).
//var certificateNode = signNode.SelectSingleNode(".//ds:X509Certificate", ns);
//var Certificate = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(certificateNode.InnerText));
// checking signature
bool isSigned = signedXml.CheckSignature(Certificate, true);
if (!isSigned)
{
throw new Exception("Certificate and signature mismatch!");
}
// If you extracted the signature, you would check the issuer here
// Here is the validation of the response
// Some of this might be unnecessary in your case, or might not be enough (especially if you plan to use SAML for more than just SSO)
var statusNode = responseNode.SelectSingleNode("samlp:Status/samlp:StatusCode", ns);
if (statusNode.Attributes["Value"].Value != "urn:oasis:names:tc:SAML:2.0:status:Success")
{
throw new Exception("Incorrect status code!");
}
var conditionsNode = assertionNode.SelectSingleNode("saml:Conditions", ns);
var audienceNode = conditionsNode.SelectSingleNode("//saml:Audience", ns);
if (audienceNode.InnerText != "Name of your app on the IDP")
{
throw new Exception("Incorrect audience!");
}
var startDate = XmlConvert.ToDateTime(conditionsNode.Attributes["NotBefore"].Value, XmlDateTimeSerializationMode.Utc);
var endDate = XmlConvert.ToDateTime(conditionsNode.Attributes["NotOnOrAfter"].Value, XmlDateTimeSerializationMode.Utc);
if (DateTime.UtcNow < startDate || DateTime.UtcNow > endDate)
{
throw new Exception("Conditions are not met!");
}
var fields = new Dictionary<string, string>();
var userId = assertionNode.SelectSingleNode("//saml:NameID", ns).InnerText;
var userName = assertionNode.SelectSingleNode("//saml:Attribute[#Name=\"urn:oid:1.2.840.113549.1.9.1\"]/saml:AttributeValue", ns).InnerText;
// you can also extract some of the other fields in similar fashion
var result = new AuthenticationResult(true, ProviderName, userId, userName, fields);
return result;
}
}
Then I just registered my client in App_Start\AuthConfig.cs using OAuthWebSecurity.RegisterClient and then I could reuse my existing external login code (which was originally made for OAUTH). For various reasons my SAML callback was a different action than my OAUTH callback. The code for this action was more or less this:
[AllowAnonymous]
public ActionResult Saml(string returnUrl)
{
Session["SAMLResponse"] = Request.Form["SAMLResponse"];
return Redirect(Url.Action("ExternalLoginCallback") + "?__provider__=mySAML");
}
Additionally OAuthWebSecurity.VerifyAuthentication didn't work with my client too well, so I had to conditionally run my own verification in the OAUTH callback.
AuthenticationResult result = null;
if (Request.QueryString["__provider__"] == "mySAML")
{
result = new mySAMLClient().VerifyAuthentication(HttpContext);
}
else
{
// use OAuthWebSecurity.VerifyAuthentication
}
This probably all looks very weird and might differ greatly in case of your IDP, but thanks to this I was able to reuse most of the existing code for handling external accounts.
I want to show my user feed on my website and what I intend to do is to authenticate my own user account each time a user visits the page, and in that way buypass that the user have to log in to his instagram account.
My problem is that I'm having a hard time retrieving the instagram access token through a HttpWebRequest..
See the following NON working code sample:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.instagram.com/oauth/authorize?client_id=xxxxxxxxxxxxxxxxxxxxxx&redirect_uri=http://mywebsite.com&response_type=token");
request.Method = "POST";
request.AllowAutoRedirect = false;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string redirectUrl = response.ResponseUri.ToString();
HttpContext.Current.Response.Write(redirectUrl);
HttpContext.Current.Response.End();
If I paste the url in my browser I get a redirect to http://mysite.com/#access_token=xxxxxxxxxxxxxx and everything seems fine, but when I try to execute the code above, I can't retrieve the correct uri due to some in between redirects before the final url.
Any help would be much appriciated..
I recommend you to use Instasharp library. InstaSharp is a C# library that wraps the Instagram API and makes it easy to write applications with Instagram data. It has a very easy method to get access token for a user. Check its API.
Unfortunately the documentation for Instasharp currently provided has a few errors. I.e. The documentation says OAuthInfo, when such a class does not exist.
Here is some code that works for me.
Notice you don't seem to need to pass a User Object at all (not sure why you would need to anyway)
Also note, that the authenticated and non authenticated methods allow you pass different params, count being the most important one. I've noticed that regardless of the count you pass, an arbitrary number of results is returned, e.g. 33 for authenticated and 13 for authenticated for the same search term. InstagramResult is my wrapper class for the object and Config holds the InstagramAuthorisationModel and InstagramAuthorisationModel holds the static keys created when signing up for a developer account.
public class InstagramService : IInstagramService
...
public InstagramConfig Config
{
get{return new InstagramConfig("https://api.instagram.com/v1", "https://api.instagram.com/oauth", InstagramAuthorisationModel.ApplicationId, InstagramAuthorisationModel.Secret, InstagramAuthorisationModel.RedirectUri);}
}
private AuthInfo UserAuthInfo()
{
return new AuthInfo()
{
// User =new UserInfo(){},
Access_Token = GetInstagramAccessToken()
};
}
public string GetInstagramAccessToken()
{
return _socialMediaRepository.GetInstagramAccessToken(_userApiKey);
}
public List<InstagramResult> Search(string searchTag, int count)
{
var auth = UserAuthInfo();
var tags = new InstaSharp.Endpoints.Tags.Authenticated(Config, auth);
var searchresult = tags.Recent(searchTag);
return searchresult.Data.Select(media => new InstagramResult()
{
Media = media,
image = media.Images.LowResolution.Url
})
.ToList();
}
..
I`m a little bit confused about the Facebook Graph API.
First, I created an app on the developers page and then I autorized my app with a URL like this:
www.facebook.com/dialog/oauth?client_id=MY_CLIENT_ID&redirect_uri=http://www.facebook.com/connect/login_success.html&type=user_agent
Ok... after this the page returned an URL like this one:
www.facebook.com/connect/login_success.html#access_token=ACCESS_TOKEN&expires_in=5171411&code=CODE
I realized that the ACCESS_TOKEN returned is always the same. So i used to search for users, like this:
graph.facebook.com/search?q=QUERY_SEARCH&type=user&access_token=ACCESS_TOKEN
I believe all the URLs above are correct.
My doubt is: i don't know how to use the long-live token (actually I dont even know if the returned token is a long-lived one). The same token is always returned for me when I use those URLs, so I always use the same ACCESS_TOKEN.
But as i read on the Facebook Graph page, a token can't be active forever anymore... they now expire.
How do I know if i have a long live token or not? When a token expire how can I "refresh" it?
I was trying to follow the documentation but I`m totally lost...
developers.facebook.com/roadmap/offline-access-removal/
This page says that exists an "deprecate offline_acess" on the advanced settings menu... but it doens't!
So... i don't know how to manage tokens when they expire or how to know if i`m using a long-lived token
I think the general idea is that your access token will last a month or so, and when it stops working you need to request a new one.
I have a method like this to fetch a new one:
public static class GraphApiRequestProcessor
{
public static string GetNewAccessToken( CancellationToken cancellationToken )
{
const string tokenUrlPattern = #"https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=client_credentials";
string tokenUrl = string.Format( tokenUrlPattern, Settings.FacebookAppId, Settings.FacebookAppSecret );
using( var client = new WebClient() )
{
// allows cancellation while executing request
using( cancellationToken.Register( client.CancelAsync ) )
{
using( var data = client.OpenRead( tokenUrl ) )
{
using( var reader = new StreamReader( data ) )
{
string response = reader.ReadToEnd();
int index = response.IndexOf( "=", StringComparison.InvariantCultureIgnoreCase );
string code = response.Substring( index + 1 );
return code;
}
}
}
}
}
}
You can check when your access token is going to expire on Access Token Debugger.
You can get long-lived access tokens by using this api where you need to enter the short lived access token.
https://graph.facebook.com/oauth/access_token?
client_id=APP_ID&
client_secret=APP_SECRET&
grant_type=fb_exchange_token&
fb_exchange_token=EXISTING_ACCESS_TOKEN
where this will return the new long-lived access token. (which will have an expiry period of 2 months.)