Gigya Job Openings

Field-Level Data Permissions via API Manager

Skip to end of metadata
Go to start of metadata

Overview

You may require setting permissions on the data field level, especially when working with third-party vendors who should only have access to specific fields in the accounts database. In this case, you can use an API manager to create an API proxy and define its logic. Apigee is one example of an API proxy platform which can give you this capability. 

Below is a code example and guidelines for using an Apigee API proxy to allow data-level permissions for the following Gigya APIs: 

Data Flow Overview

Demo

We have configured a live example which demonstrates calling accounts.search with the following query:

select "*" from accounts where profile.firstName=Javier

In the first example, we configured the call without filters, as you usually would using Gigya. 

In the second example, the call is passed via an API proxy created in Apigee and includes only profile.firstName profile.email profile.lastName isActive  and  UID so that any other field is filtered out. 

Check it out: 

 

             

 

This is the configuration in the Node.js file for the filtered search:

accounts.search
exports.applicationKey = '...';
exports.applicationSecret = '...';
exports.includedFields = ['profile.firstName','profile.email','profile.lastName','isActive', 'UID'];
exports.excludedFields = [];
exports.protocol = 'https';
exports.defaultData_Center_ID = 'us1';
exports.endpointStart = '://accounts.';
exports.endpointEnd = '.gigya.com/accounts.search';
exports.userKey = "...";
exports.dataCenters = ['eu1','us1','au1','ru1'];

Implementation

This guide assumes that you have already created an application key and secret in the Gigya Console. If you have not, see the appendix for instructions. 

Most API managers, including Apigee, offer powerful authorization capabilities, for example using OAuth 2.0. We recommend implementing authorization with your API proxy for security reasons.

1. Download Files

Download and unzip the following folder: Apigee files

2. Create an Apigee API Proxy

  1. Go to https://login.apigee.com/login
  2. If you do not yet have an account, click Create account and complete the registration process. Otherwise, login. 
  3. In the screen that opens, activate the API Management option. Complete the activation process via the confirmation email. 
  4. Go to https://enterprise.apigee.com/
  5. Under APIs, select API Proxies
  6. Click +API Proxy:
  7. In the Build a Proxy menu, select the Node.js App option and click Next.
  8. In the Details section, enter the Proxy Name, such as getAccountInfo.
  9. Under Source, select Existing


  10. Click Choose Files to upload the sample node.js files into the new API proxy. 
  11. Upload a pair of the files provided in the first step: for each API proxy, a script file and a configuration file. The script files are named after the relevant Gigya APIs, and the configuration files are named "config*". The Apigee Node sample folder is organized in sub-folders by the Gigya APIs.
  12. Enter a description and click Next
  13. In the Security section, select Pass through (none) and click Next. Other options may be available, depending on your Apigee account tier. 
  14. Under Virtual Hosts, select secure and click Next. 
  15. Select whether to build a prod or test environment or both, and click Build and Deploy.
  16. Click View <Your API Name> proxy in the editor.

3. Configure the API Proxy

For each of the API proxies:

  1. Go to the Developer tab. 
  2. On the left hand side, under Target Endpoints, select the PostFlow file. 
  3. In the code editor, locate the <ResourceURL> tag at the bottom, nested under the <ScriptTarget> tag. Make sure the node references the script file and not the configuration file. For example, if you are configuring the accounts.getAccountInfo API, make sure the following appears:

    <ScriptTarget>
         <ResourceURL>node://getAccountInfo.js</ResourceURL>
    </ScriptTarget>
  4. Under Scripts. open the configuration file and enter the following configurations: 
  • exports.applicationKey: Your site's API key.  You can copy the API key from the home page of the Gigya Console
  • exports.applicationSecret: The Gigya application secret.
  • exports.userKey: The user key associated with the application.
  • exports.includedFields: An array of the fields to which this application has access permissions using the Gigya API call.
  • exports.excludedFields: An array of fields for which this application is denied access. 

Appendix: Creating a Gigya Application 

Application and User Keys

Using SAP Customer Data Cloud APIs with an Application or User Key

REST requests should be made using an application key and application secret. This is true also for requests made by third parties. Alternatively, you can use a user key and secret. These are subject to the user key's permissions and are logged for auditing purposes.

When you pass a request across HTTPS, include the site's API Key and the application key and secret (or user key and secret). For example:

 

https://...?apiKey=[SITE_API_KEY]&userKey=[APPLICATION_KEY]&secret=[APPLICATION_SECRET]

All calls should be made over HTTPS.

 

For more information about user keys, including instructions for finding your user key in the Console, see Using the User Key.

Instructional Video

If you have an SAP logon, you can watch an instructional video about managing applications here.

Creating an Application Key

You can create multiple applications, each with its own permissions, and give groups of users access to these various applications. Each application has a userKey and secret that is used when making REST calls to Gigya API Endpoints.

Another benefit of using a userKey and secret is that the user does not have to construct or check signatures, as all requests are conducted over HTTPS.

To manage your SAP Customer Data Cloud Applications:

  1. Login to the Console.
  2. Navigate to the Admin tab.
  3. Select Applications.
  4. Once on the Applications page, press Create New Application and follow the on-screen prompts.

    If you are going to use the RSA portion of the application, instead of or in addition to the userKey and secret, you MUST copy and save your RSA Private Key now. The RSA Private Key is only available inside the pop-up modal after the application is created. We do not store the RSA secret, if you do not copy it prior to closing the window, it will be necessary to generate a new key-pair. See Using an RSA Key for more information.





  5.  Once the app is created you can view the Apps userKey and secret by clicking the Edit icon, which will take you to the apps Edit Application page.



  6.  You can disseminate this userKey and secret to users whom you want to attain the privileges associated with this app. Users will use this userKey and replace the secret parameter in the request with the secret associated to this key.
  7. If at any time you want to revoke access for users using this Application, simply delete it from your account and all future attempts to use this userKey and secret will fail.

 

curl Code Example

curl https://accounts.us1.gigya.com/accounts.search 
 --data-urlencode "apiKey=3_mKxxxxXXXXXXXXxxxxxxxxXXXXxxXxxxxxxxxxxxxxxxxxxXXXXXXXXXXXXxxxxx" 
 --data-urlencode "userKey=AJxXXxXxxX2X" 
 --data-urlencode "secret=X73xXXXXXxxxxXXXxxxXXXx656767Xxx" 
 --data-urlencode "format=json" 
 --data-urlencode "query=select UID, identities.provider, identities.providerUID from accounts limit 10"

In the above example, the secret is the secret associated with the userKey, not the account secret located in the Console homepage.

 

Adding An Application Key

You can add existing applications similar to creating a new application. Simply click the Add Existing Application button and enter the userKey associated with the application, select a Permissions group to the application and press Add. If the import was successful you will get a notification (with the name of the application as it exists in the parent account):

 

It is important to note that the data associated with applications are per API key and will show blank if attempting to Edit them while viewing the Admin tab from a different API key.

 

RSA Keys

When creating a new application in the Console, a userKey and secet will be assigned to the new application. In addition, an RSA key-pair is assigned to the application. The RSA Private key is used for generating an HTTP Bearer token for use in place of the application key and secret to sign API requests.

 

The RSA Private Key is only available inside the pop-up modal when the application is created. We do not store the RSA key, so if you lose it, you will need to generate a new key-pair with a new Private key.

 

 

Using SAP Customer Data Cloud APIs with an RSA key-pair

It is possible to sign a request using an HTTP Bearer token instead of the app key and secret. Expand the link below for an example implementation using C#.

For additional information on Bearer tokens, see https://tools.ietf.org/html/rfc6750.

 

 Expand for .NET code example
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using demo_site.Models;
using demo_site.Utils;
using JsonDiffPatchDotNet;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace demo_site.Controllers
{
    public class HomeController : Controller
    {
        public static readonly string apikey = "<Gigya/CDC-API-Key>";
        public static HttpClient _httpClient = new HttpClient();

        private string key = @"-----BEGIN RSA PRIVATE KEY-----
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxxxxxxxxXXXXXXXXXXxxxxxxxxxXXxxXxXxxXxxxXXXXxxxxXxXxXxxxXXXxxX
yyYYydyyasuyydgde32gyguygyugy3g4gug24ygy3g3ug4y42uy5g6yg4uyggryh
xxxXXxxXxxxxXXxXXXXxxXXxXxxXXXXXXXXXXXXXxxxXXXXXXXXXxx==
-----END RSA PRIVATE KEY-----";


        [HttpPost]
        public async Task<IActionResult> Login([FromBody] LoginRequest req)
        {

            var uid = await ValidateJWT(req.id_token);

            var asyncJwt = CreateJWT();

            var httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", asyncJwt);
            var response = await httpClient.GetStringAsync($"https://accounts.us1.gigya.com/accounts.getAccountInfo?apikey={apikey}&uid={uid}");


            return Json(response);
        }

        public Task<IActionResult> Schema()
        {
            return getConfig("accounts.getSchema");
        }

        public Task<IActionResult> Policy()
        {
            return getConfig("accounts.getPolicies");
        }


        private async Task<IActionResult> getConfig(string api)
        {
            var datacenters = new string[] { "us1", "eu1", "au1" };

            var schemasResults = await Task.WhenAll(datacenters
                            .Select(x => new HttpRequestMessage(HttpMethod.Get, $"https://accounts.{x}-st2.gigya.com/{api}?apikey={apikey}"))
                            .Select(x =>
                            {
                                x.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", CreateJWT());
                                return x;
                            })
                            .Select(x => _httpClient.SendAsync(x))
                            .Select(async x => await (await x).Content.ReadAsStringAsync())
                            .Select(async x =>
                            {
                                var o = JObject.Parse(await x);
                                o.Remove("callId");
                                o.Remove("time");
                                return o;
                            }));

            var jdp = new JsonDiffPatch();
            var us1Schema = schemasResults.First();
            var schemas = schemasResults
                    .Skip(1)
                    .Select(x => jdp.Diff(us1Schema, x));

            return Json(schemas);
        }

        private string CreateJWT()
        {
            var rsaProvider = RsaUtils.DecodeRsaPrivateKey(key);

            var handler = new JwtSecurityTokenHandler();
            handler.SetDefaultTimesOnTokenCreation = false;
            handler.OutboundClaimTypeMap["jti"] = Guid.NewGuid().ToString();
            var rsakey = new RsaSecurityKey(rsaProvider);
            var descriptor = new SecurityTokenDescriptor
            {
                SigningCredentials = new SigningCredentials(rsakey, "RS256"),
                IssuedAt = DateTime.Now,
                Claims = new Dictionary<string, object>
                {
                    [JwtRegisteredClaimNames.Jti] = Guid.NewGuid().ToString()
                }
            };

            var token = handler.CreateJwtSecurityToken(descriptor);
            token.Payload.TryAdd("jti", Guid.NewGuid().ToString());
            token.Header["kid"] = "hdahjKJHu23u";


            var tokenString = handler.WriteToken(token);

            return tokenString;
        }

        private async Task<string> ValidateJWT(string idToken)
        {
            var httpClient = new HttpClient();

            var response = await httpClient.GetStringAsync("https://accounts.us1.gigya.com/accounts.getJWTPublicKey?apikey=<Gigya/CDC-API-Key>&v2=true");

            var jwks = new JsonWebKeySet(response);

            var parameters = new TokenValidationParameters
            {
                ValidateAudience = false,
                ValidateIssuer = true,
                ValidIssuer = "https://fidm.gigya.com/jwt/<Gigya/CDC-API-Key>",
                ValidateLifetime = true,
                IssuerSigningKeys = jwks.Keys
            };

            var handler = new JwtSecurityTokenHandler();

            handler.InboundClaimTypeMap.Clear();

            SecurityToken jwt;
            ClaimsPrincipal claimsPrincipal = handler.ValidateToken(idToken, parameters, out jwt);
            var uid = claimsPrincipal.FindFirst("sub").Value;
            return uid;
        }

        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Global()
        {
            return View();
        }

        public IActionResult Replay()
        {
            return View();
        }

        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";

            return View();
        }

        public IActionResult Contact()
        {
            ViewData["Message"] = "Your contact page.";

            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }

    }
}

 

 

 

 

 

  • No labels