Skip to content

sk8tz/quantfabric

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Quantfabric Material

A cross platform framework for collecting personal data from various sources including web apis (OAuth), bluetooth devices, and smartphone embedded sensors (GPS, SMS, etc)

NuGet

You can get Material by grabbing the latest NuGet package currently available for .NET 4.5, Xamarin.Android, Xamarin.iOS, and Windows UWP (Xamarin.Forms in progress)

What is your usage scenario?

  1. I have a mobile/desktop app and I want to use an OAuth1 authentication provider (ie Login with Twitter)
  2. I have a mobile/desktop app and I want to use an OAuth2 authentication provider (ie Login with Facebook)
  3. I have a web application and I want to use an OAuth1 authentication provider (ie Login with Twitter)
  4. I have a web application and I want to use an OAuth2 authentication provider (ie Login with Facebook)
  5. I have a mobile, web or desktop app and I want to refresh my OAuth2 credentials
  6. I have a mobile, web or desktop app and I want to access OAuth protected resources (ie access to my users Tweets)
  7. I want to test OAuth2 authentication workflows for an OAuth2 provider I am creating
  8. I want to connect to a Bluetooth GATT device
  9. I want to consume a device resource (SMS or GPS)

List of OAuth1 Providers

List of OAuth2 Providers

  • Facebook (token expires, no refresh token provided)
  • Fitbit (token expires, refresh token provided)
  • Foursquare (token does not expire)
  • Google (token expires, refresh token provided)
  • LinkedIn (token does not expire)
  • Rescuetime (token does not expire)
  • Runkeeper (token does not expire)
  • Spotify (token expires, refresh token provided)
  • TwentyThreeAndMe (token expires, refresh token provided)
  • Create a Provider

List of Requests

  • FacebookFeed
  • FacebookEvent
  • FacebookFriend
  • FacebookPageLike
  • FatsecretMeal
  • FitbitIntradayHeartRate
  • FitbitIntradayHeartRateBulk
  • FitbitIntradaySteps
  • FitbitIntradayStepsBulk
  • FitbitProfile
  • FitbitSleep
  • FoursquareCheckin
  • FoursquareFriend
  • FoursquareTip
  • GoogleGmail
  • GoogleGmailMetadata
  • LinkedinPersonal
  • LinkedinUpdate
  • RescuetimeAnalyticData
  • RunkeeperFitnessActivity
  • SpotifySavedTrack
  • TwentyThreeAndMeGenome
  • TwentyThreeAndMeUser
  • TwitterFavorite
  • TwitterFollower
  • TwitterFollowing
  • TwitterMention
  • TwitterReceivedDirectMessage
  • TwitterSentDirectMessage
  • TwitterRetweetOfMe
  • TwitterTimeline
  • TwitterTweet
  • WithingsWeighin
  • Create a Request

Web App Authentication Provider (OAuth1)

To obtain the redirect url for authorization on the resource providers server:

//OPTIONALLY: inject the OAuth1Web instance into your class or method
string consumerKey = "YOUR CONSUMER KEY";
string consumerSecret = "YOUR CONSUMER SECRET"
string callbackUri = "HTTP://YOURCALLBACKURI";

OAuth1Web<Twitter> oauth = new OAuth1Web<Twitter>(
	consumerKey, 
	consumerSecret, 
	callbackUri);

string userId = "SOMEUSERID";  //some unique identifier stored in a cookie or session state

Uri twitterLoginEndpoint = await oauth
	.GetAuthorizationUriAsync(userId)
	.ConfigureAwait(false);

To handle the callback to your server (assume this callback is an endpoint TwitterCallback within an .NET MVC Controller):

public async Task<ActionResult> TwitterCallback()
{
	//SUGGESTED: inject the OAuth1Web instance into your controller
	string clientId = "YOUR CLIENT ID";
	string clientSecret = "YOUR CLIENT SECRET";
	string callbackUri = "HTTP://YOURCALLBACKURI";
    
	OAuth1Web<Twitter> oauth = new OAuth1Web<Twitter>(
		clientId, 
		clientSecret,
		callbackUri);

	string userId = Request.Cookies["userId"];
	string url = ControllerContext.HttpContext.Request.Url;
    
	OAuth1Credentials intermediateCredentials = oauth
		.ParseAndValidateCallback(url, userId);
		
	OAuth1Credentials credentials = await oauth
		.GetAccessTokenAsync(intermediateCredentials)
		.ConfigureAwait(false);
	
	//you've got the credentials!

	return RedirectToAction("SOMEREDIRECTPAGE"); 
}

* If you are working in an environment with multiple servers and you do not use sticky sessions, see the advanced topic Creating Your Own Security Strategy

Web App Authentication Provider (OAuth2)

To obtain the redirect url for authorization on the resource providers server:

//OPTIONALLY: inject the OAuth2Web instance into your class or method
string clientId = "YOUR CLIENT ID";
string clientSecret = "YOUR CLIENT SECRET";
string callbackUri = "HTTP://YOURCALLBACKURI";

OAuth2Web<Facebook> oauth = new OAuth2Web<Facebook>(
	clientId, 
	clientSecret, 
	callbackUri);
    
string userId = "SOMEUSERID";  //some unique identifier stored in a cookie or session state

Uri facebookLoginEndpoint = await oauth
	.GetAuthorizationUriAsync(userId)
	.ConfigureAwait(false);

If you want to make a subsequent request for a protected resource add scopes for each request you intend to make. For example:

Uri facebookLoginEndpoint = await oauth
	.AddScope<FacebookFeed>()
	.AddScope<FacebookFriend>()
	.GetAuthorizationUriAsync(userId)
	.ConfigureAwait(false);

To handle the callback to your server (assume this callback is an endpoint FacebookCallback within an .NET MVC Controller and that the userId is stored in a cookie):

public async Task<ActionResult> FacebookCallback()
{
	//SUGGESTED: inject the OAuth2Web instance into your controller
	string clientId = "YOUR CLIENT ID";
	string clientSecret = "YOUR CLIENT SECRET";
    string callbackUri = "HTTP://YOURCALLBACKURI";
	
	OAuth2Web<Facebook> oauth = new OAuth2Web<Facebook>(
		clientId, 
		clientSecret, 
		callbackUri);

	var userId = Request.Cookies["userId"];
	var url = ControllerContext.HttpContext.Request.Url;
    
	OAuth2Credentials intermediateCredentials = oauth
		.ParseAndValidateCallback(url, userId);
	
	OAuth2Credentials credentials = await oauth
		.GetAccessTokenAsync(intermediateCredentials)
		.ConfigureAwait(false);
	
	//you've got the credentials!

	return RedirectToAction("SOMEREDIRECTPAGE"); 
}

* If you are working in an environment with multiple servers and you do not use sticky sessions, see the advanced topic Creating Your Own Security Strategy

Mobile/Desktop Authentication Provider (OAuth1)

When creating your app with the resource provider use localhost as the callback uri with an uncommon port number. For example http://localhost:33533/twitter

public async Task MyAuthenticationMethod()
{
	string consumerKey = "YOUR CONSUMER KEY";
	string consumerSecret = "YOUR CONSUMER SECRET";
	string callbackUri = "http://localhost:33533/twitter";
	
	OAuth1App<Twitter> oauth1 = new OAuth1App<Twitter>(
		consumerKey, 
		consumerSecret, 
		callbackUri);
		
	OAuth1Credentials credentials = await oauth1
		.GetCredentialsAsync()
		.ConfigureAwait(false);
}

Mobile/Desktop Authentication Provider or for Testing (OAuth2)

When creating your app with the resource provider use localhost as the callback uri with an uncommon port number. For example http://localhost:33533/facebook

public async Task MyAuthenticationMethod()
{
	string clientId = "YOUR CLIENT ID";
	string callbackUri = "http://localhost:33533/facebook";
	
	OAuth2App<Facebook> oauth2 = new OAuth2App<Facebook>(
		clientId, 
		callbackUri);
	
	OAuth2Credentials credentials = await oauth2
		.GetCredentialsAsync()
		.ConfigureAwait(false);
}

For subsequent requests of protected resource add scopes for each request type. For example:

OAuth2Credentials credentials = await oauth
	.AddScope<FacebookFeed>()
	.AddScope<FacebookFriend>()
	.GetCredentialsAsync()
	.ConfigureAwait(false);

The 'code' workflow can also be used in the event a long lived access token is desired and the client secret can be embedded in the application (testing purposes, etc):

public async Task MyAuthenticationMethod()
{
	string clientId = "YOUR CLIENT ID";
	string clientSecret = "YOUR CLIENT SECRET";
	string callbackUri = "HTTP://YOURCALLBACKURI";
	
	OAuth2App<Facebook> oauth2 = new OAuth2App<Facebook>(
		clientId, 
		clientSecret, 
		callbackUri);
	
	OAuth2Credentials credentials = await oauth2
		.GetCredentialsAsync()
		.ConfigureAwait(false);
}

By default OAuth1App and OAuth2App use an embedded browser (WebView on Android, UIWebView on iOS, WebView on UWP) to complete the oauth workflow. To use a dedicated (system) browser see the section on Dedicated Mobile Browsers.

OAuth2 Refresh Token

If the authentication provider has an access token that expires and provides a refresh token (see list of OAuth2 providers), that refresh token can be exchanged for a new, valid access token:

OAuth2Credentials googleCredentials = CREDENTIALS_I_GOT_EARLIER;

if (googleCredentials.IsTokenExpired)
{
	googleCredentials = await new OAuth2Refresh<Google>()
            						.RefreshCredentialsAsync(
                						expiredToken)
            						.ConfigureAwait(false);
}

Accessing Protected Resources

After gathering the necessary OAuth1Credentials by using either the OAuth 1 web or OAuth 1 desktop workflows, or OAuth2Credentials by using either the OAuth 2 web or OAuth 2 desktop workflows, a request for a protected resource can be made.

OAuth1Credentials twitterCredentials = CREDENTIALS_I_GOT_EARLIER;

var response = await new OAuthRequester(twitterCredentials)
	.MakeOAuthRequestAsync<TwitterTweet, TwitterTweetResponse>()
	.ConfigureAwait(false);

If the request needs to be customized an instance of the request class can be created

OAuth1Credentials twitterCredentials = CREDENTIALS_I_GOT_EARLIER;

var request = new TwitterTweet();
request.Count = 100;
var response = await new OAuthRequester(twitterCredentials)
	.MakeOAuthRequestAsync<TwitterTweet, TwitterTweetResponse>(request)
	.ConfigureAwait(false);

Bluetooth

BluetoothCredentials credentials = await new BluetoothApp<Mioalpha>()
                						.GetBluetoothCredentialsAsync()
                						.ConfigureAwait(false);
                                        
BluetoothResponse result = await new BluetoothRequester()
                				.MakeBluetoothRequestAsync<MioHeartRate>(credentials)
                				.ConfigureAwait(false);

To connect to a custom Bluetooth GATT device, see Creating a Bluetooth Provider and Request

Device (SMS/GPS)

To request the current GPS position:

GPSResponse result = await new GPSRequester()
						.MakeGPSRequestAsync()
                		.ConfigureAwait(false);

(Android only) To request a list of SMS currently in inbox or sent:

SMSResponse results = await new SMSRequester()
                		.MakeSMSRequestAsync()
                		.ConfigureAwait(false);

The SMS results can also be filtered by a date:

DateTime dateFilter = System.DateTime.Today;
SMSResponse results = await new SMSRequester()
                		.MakeSMSRequestAsync(dateFilter)
                		.ConfigureAwait(false);

Provider Specific Notes

Rescuetime

Since rescuetime requires an HTTPS endpoint and the current HttpServer implementation does not handle HTTPS you will see an error when your Rescuetime callback request comes back, when using a desktop workflow. The current workaround is for the user to manually update the url in the browser window, changing HTTPS into HTTP and then hitting 'return'.

Advanced Topics

Using a dedicated (system) browser on a mobile device (Android, iOS, UWP)

WORK IN PROGRESS: Currently performing this configuration only works with Google and requires particular setup steps in the configurations of the iOS, Android, or UWP project to properly receive the protocol based callback

In some mobile device situations a dedicated browser (Chrome on Android, Safari on iOS, IE on Windows) may be desired for the workflow. If that is the case an optional parameter can be passed to indicate the browser type:

	OAuth1App<Twitter> oauth1 = new OAuth1App<Twitter>(
		consumerKey, 
		consumerSecret, 
		callbackUri,
        AuthenticationInterfaceEnum.Dedicated);

	OAuth2App<Facebook> oauth2 = new OAuth2App<Facebook>(
		clientId, 
		clientSecret, 
		callbackUri,
        AuthenticationInterfaceEnum.Dedicated);

Creating your own security parameter repository

During the OAuth2 workflow a InMemoryCryptographicParameterRepository object is used to store the "state" parameter that is round-tripped to the resource provider. This implementation stores the generated parameters in a static variable in the current app domain. This is problematic in a multi-server scenario without sticky sessions. To remedy this create an implementation of ICryptographicParameterRepository that utilizes some other mechanism of storing the parameters (database session cache, cookies, etc). For example:

public class CookieCryptographicParameterRepository : 
    ICryptographicParameterRepository
{
    private readonly HttpCookieCollection _cookies;

    public CookieCryptographicParameterRepository(
        HttpCookieCollection cookies)
    {
        _cookies = cookies;
    }

    public void SetCryptographicParameterValue(
        string userId, 
        string parameterName, 
        string parameterValue, 
        DateTimeOffset timestamp)
    {
        var cookie = new HttpCookie(userId + parameterName);
        cookie.Values["value"] = parameterValue;
        cookie.Values["timestamp"] = timestamp.ToString();

        _cookies.Add(cookie);
    }

    public Tuple<string, DateTimeOffset> GetCryptographicParameterValue(
        string userId,
        string parameterName)
    {
        var cookie = _cookies[userId + parameterName];

        if (cookie == null)
        {
            return default(Tuple<string, DateTimeOffset>);
        }
        else
        {
            return new Tuple<string, DateTimeOffset>(
                cookie["value"], 
                DateTimeOffset.Parse("timestamp"));
        }
    }

    public void DeleteCryptographicParameterValue(
        string userId, 
        string parameterName)
    {
        _cookies.Remove(userId + parameterName);
    }
}

When creating an instance of OAuth2WebFacade, pass an instance of the repository to OAuthSecurityStrategy and then into the facade:

string clientId = "YOUR CLIENT ID";
string clientSecret = "YOUR CLIENT SECRET";
string callbackUri = "HTTP://YOURCALLBACKURI";
string userId = "SOMEUSERID";
OAuthSecurityStrategy strategy = new OAuthSecurityStrategy(
                    new CookieCryptographicParameterRepository(
                        HttpContext.Response.Cookies), 
                    TimeSpan.FromMinutes(2)))

OAuth2WebFacade<Facebook> oauth2 = new OAuth2WebFacade<Facebook>(
	clientId, 
	clientSecret, 
	userId,
	callbackUri,
	strategy); 

Creating a Bluetooth Provider and Request

Create a resource provider class to acquire the device address:

using Material.Infrastructure.Credentials;
using Material.Metadata;

namespace Material.Infrastructure.ProtectedResources
{
	[CredentialType(typeof(BluetoothCredentials))]        
	public partial class MyBluetoothDevice : BluetoothResourceProvider
	{
	}
}

Create a request to access the GATT characteristic. The static classes BluetoothServices and BluetoothCharacteristics contain the assigned numbers for all approved bluetooth services and characteristics respectively. A conversion function should be written on a characteristic by characteristic basis to convert the raw byte[] reading into a string.

using System;
using Material.Infrastructure.Bluetooth;
using Material.Infrastructure.ProtectedResources;
using Material.Metadata;

namespace Material.Infrastructure.Requests
{       
    [ServiceType(typeof(MyBluetoothDevice))]        
    public partial class MyBluetoothDeviceBloodPressureRate : BluetoothRequest
    {
        public override BluetoothSpecification Characteristic => 
            BluetoothCharacteristics.BloodPressureMeasurement;

        public override Func<byte[], string> CharacteristicConverter =>
            (data) => data.ToString();

        public override BluetoothSpecification Service => 
            BluetoothServices.BloodPressure;
    }
}

Creating an OAuth Resource Provider

TODO

Creating an OAuth Request

TODO

About

a crossplatform framework for collecting data from various sources

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages