OAuth Facility Demo

What is OAuth? OAuth is a simple technology that allows for secure access of data that is owned by users. It allows the user to physically authorize access of their own data to your facility. Using OAuth we can securely share proposals from ARIA and pass them to your own facility management system.

This demo will provide a quick overview of simple one way that your local facility proposal submission system can be integrated with ARIA to allow users to import their existing proposal data into your local system and give them the opportunity to complete any missing fields from their original submission. You will need the following login information to take you through the authorization process that your users would normally go through.

Authentication: Instruct
Username: aria@structuralbiology.eu
Password: ARIAtest

index.php

copy
<?php
// we give this to the browser to issue requests and things - this is your unique client ID that lets ARIA know where your request is coming from
$client_id = '4Fgp5SLGRqxaVL5Z534Gou7bjoTjhbyFc3qaoZg5k865GXvEoTG8je4PtMVYKo6m';
// keep this one on the server for security (used for generating access tokens)
$client_secret = 'oLAQPIiKYqLPSoQhmCcPnEPf5GQqJIbtTbfafKk83rKk62OHZmXIRhjon14sA7EP';

$OAservice = 'https://www.structuralbiology.eu/ws/oauth/';

// define a unique state string for security
session_start();
if(!isset($_SESSION['OAstate']))
  $_SESSION['OAstate'] = rand(1000000, 9999999);

// if we have an authentication token we want to process it into an access code
if(isset($_GET['code']) && isset($_GET['state'])){
  $code = $_GET['code'];
  $state = $_GET['state'];
  
  // quickly sanitize input
  if(!preg_match('/[A-Za-z0-9]+/', $code))
    exit('Authentication code invalid');
  if(!preg_match('/[A-Za-z0-9]+/', $state))
    exit('State is invalidx');
  
  // verify the state before continuing on
  if($state != $_SESSION['OAstate'])
    exit('State is invalid');
  
  // manually build OAuth packet - this can be replaced for an off-shelf solution
  $OApacket = array(
    'client_id' => urlencode($client_id),
    'client_secret' => urlencode($client_secret),
    'grant_type' => 'authorization_code',
    'code' => urlencode($code)
  );
  
  // start curl connection
  $ch = curl_init();
  curl_setopt( $ch, CURLOPT_URL, $OAservice.'token' );
  curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
  curl_setopt( $ch, CURLOPT_AUTOREFERER, true );
  curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true );
  curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
  curl_setopt( $ch, CURLOPT_CAINFO, "cacert.pem" ) ;
  curl_setopt( $ch, CURLOPT_MAXREDIRS, 10 );
  
  // add OAuth packet and define HTTP POST
  curl_setopt( $ch, CURLOPT_POST, true );
  curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query($OApacket) );
  
  // run curl and capture output
  if(!$content = curl_exec( $ch ))
    exit('Could not fetch access token');
  curl_close ( $ch );
  
  // convert JSON into object
  if(!$authorization = json_decode($content))
    exit('JSON parsing error');
  if($authorization->access_token){
    // we will store access tokens as cookies for this demo, but you will want to store them somewhere more persistent (database)
    setcookie('access_token', $authorization->access_token, time()+$authorization->expires_in);
    // this refresh token actually has an expiry of 28 days, however for the demo it's useful to expire within a single day
    setcookie('refresh_token', $authorization->refresh_token, time()+60*60*24*1);
    $_COOKIE['access_token'] = $authorization->access_token;
  }
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ARIA OAuth Demo</title>
  <meta name="description" content="">
  <meta name="author" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="//fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/skeleton.css">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
  <link rel="icon" type="image/png" href="images/favicon.png">
  
  <script src="//code.jquery.com/jquery-1.12.0.min.js"></script>
  <script>
  $(function() {
    // you will probably need to get these tokens from your datastore
    var access_token = getCookie('access_token');
    
    // event for clicking the proposal import
    $("a#proposalImport").click(function(e){
      
      if(!access_token.length){
        // we need to go and get a new access token using the refresh token
        // to keep the client secret, secret, we need to pass to a local script that can contact the OAuth server itself
        $.ajax({
          url: 'refreshToken.php',
          dataType: 'json',
          method: 'POST',
          async: false,
          data: {},
          success: function(data){
            var d = new Date();
            if(data.access_token)
              access_token = data.access_token;
            else if(data.error)
              alert(data.error+'\n'+data.error_description);
          }
        });
      }
      
      if(access_token.length){
        // ok so we have an access token now, so lets prevent the link from going to get a new one
        e.preventDefault();
        // lets show the user that we're busy loading data
        $("#proposalSelector").html('<i class="fa fa-spin fa-spinner"></i> Loading EU proposals...');
        
        // now to use the access token to pull the users' proposals, this step can be asynchronous
        $.ajax({
          url: '<?php echo $OAservice; ?>proposallist',
          dataType: 'json',
          method: 'POST',
          data: {
            // first we need to define the access token, key to the OAuth transaction
            access_token: access_token,
            // next we have to define some things specific to ARIA, such as what the data format is we are requesting
            aria_response_format: 'json'
          },
          success: function(data, status){
            if(!data.error){
              // give the user some suggestion as to what to do with the list, and remove the loading text
              $("#proposalSelector").html('Choose from the following list of projects to import your data:');
              // add each proposal to the list
              $.each(data.proposals, function(i, proposal){
                $("#proposalSelector").append('<div class="proposal" data-pid="'+proposal.pid+'">'+proposal.display+'</div>');
              });
            } else
              alert(data.error+'\n'+data.error_description);
          }
        });
      }
    });
    
    // event for choosing one of the proposals now listed in our selector
    $("#proposalSelector").on('click', '.proposal', function(){
      // you may want to re-verify your access_token at this stage, but if you don't expect your user's token to expire between pulling a list of proposals, and then choosing a proposal, you should be fine
      
      $.ajax({
        url: '<?php echo $OAservice; ?>proposal',
        dataType: 'json',
        method: 'POST',
        context: $(this),
        data: {
          // first we need to define the access token, key to the OAuth transaction
          access_token: access_token,
          // next we have to define some things specific to ARIA, such as what the data format is we are requesting
          aria_response_format: 'json',
          aria_pid: $(this).data('pid')
        },
        success: function(data){
          if(!data.error){
            $(this).addClass('selected');
            // you will probably want to capture the ID internally to refer back to in the future (we do this in a hidden field within the form)
            $("#proposalForm [name='external_euid']").val(data.proposal.pid);
            // the rest of the fields need to be mapped as best to your proposal forms, more details in the documentation
            $("#proposalForm [name='title']").val(data.proposal.title);
            $("#proposalForm [name='science_case']").val(data.proposal.fields[11].data);
            $("#proposalForm [name='research']").val(data.proposal.fields[12].data);
            $("#proposalForm [name='existing_data']").val(data.proposal.fields[15].data);
          } else
            alert(data.error+'\n'+data.error_description);
        }
      });
    });
  });
  function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0; i<ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0)==' ') c = c.substring(1);
      if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
    }
    return "";
  }
  </script>

</head>
<body>
  <div class="container" style="margin-top:10%">
    <div class="row">
      <h3><img src="images/synchrotronlogo.png"> National Synchrotron Facility</h3>
      <p>The following form is an example of a local submission system within any facility wanting to import proposals from ARIA.</p>
    </div>
    <div class="row">
      <div class="four columns">
        <p>If you have an existing proposal with either Instruct or iNEXT click the below button to import your proposal data.</p>
        
        <div id="proposalSelector" style="margin-bottom:1.5em">
        </div>
        <a id="proposalImport" class="button button-primary" href="<?php echo $OAservice; ?>authorize?client_id=<?php echo $client_id; ?>&state=<?php echo $_SESSION['OAstate']; ?>&response_type=code">Import Proposal from ARIA</a>
        
      </div>
      <div class="eight columns">
        <form id="proposalForm">
          <label>Proposal Title</label>
          <input class="u-full-width" type="text" name="title">
          <label>Scientific Case</label>
          <textarea class="u-full-width" name="science_case"></textarea>
          <label>Research Programme</label>
          <textarea class="u-full-width" name="research"></textarea>
          <label>Existing Results and Data</label>
          <textarea class="u-full-width" name="existing_data"></textarea>
          <label></label>
          <hr>
          <label>Detail any sample safety concerns</label>
          <textarea class="u-full-width"></textarea>
          <label>Enter your preferred data collection date</label>
          <input class="u-full-width" type="text">
          <input type="hidden" name="external_euid">
        </form>
      </div>
    </div>
  </div>
</body>
</html>

refreshToken.php

copy
<?php
// we give this to the browser to issue requests and things - this is your unique client ID that lets ARIA know where your request is coming from
$client_id = 'nRkQrta3dgtqLkMFZx0ZqGcLXQG7UfQdsxpGIq8hYsZA8GFt83nlcQYz60tsabSp';
// keep this one on the server for security (used for generating access tokens)
$client_secret = 'hlyvxffDyQdLEGtM5f8XkGKenn8kyq25ZtTq0VpPHT2L0ltsRsgZzR7EaykZGgvA';

$OAservice = 'http://instruct-test.strubi.ox.ac.uk/update/ws/oauth/';

if(!preg_match('/[A-Za-z0-9]+/', $_COOKIE['refresh_token']))
  exit();

// verify the state before continuing on
if($state != $_SESSION['OAstate'])
  exit('{"error":"State is invalidx"}');

// manually build OAuth packet - this can be replaced for an off-shelf solution
$OApacket = array(
  'client_id' => urlencode($client_id),
  'client_secret' => urlencode($client_secret),
  'grant_type' => 'refresh_token',
  // get your refresh token from the 
  'refresh_token' => urlencode($_COOKIE['refresh_token'])
);

// start curl connection
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $OAservice.'token' );
curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $ch, CURLOPT_AUTOREFERER, true );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
curl_setopt( $ch, CURLOPT_MAXREDIRS, 10 );

// add OAuth packet and define HTTP POST
curl_setopt( $ch, CURLOPT_POST, true );
curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query($OApacket) );

// run curl and capture output
if(!$content = curl_exec( $ch ))
  exit('{"error":"Could not fetch access token"}');
curl_close ( $ch );

// convert JSON into object

if(!$authorization = json_decode($content))
  exit('{"error":"JSON parsing error"}');
if($authorization->access_token){
  // we will store access tokens as cookies for this demo, but you will want to store them somewhere more persistent (database)
  setcookie('access_token', $authorization->access_token, time()+$authorization->expires_in);
  $_COOKIE['access_token'] = $authorization->access_token;
  // store the new refresh token that we recieved too
  setcookie('refresh_token', $authorization->refresh_token, time()+28*24*60*60);
}
echo $content;