Reveal Data from 3rd Party
This guide will show you how to reveal sensitive data from a third party back to a customer without your frontend or backend code ever needing to touch the actual raw data.
Key concepts in this guide:
Getting Started
To get started, you will need a Basis Theory account.
Creating a Public Application
Next you will need a Public Application in order to create tokens, sessions and initialize our Elements libraries.
Click here to create a Public Application or login to your Basis Theory account and create a new application with the following settings:
- Name - Public App
- Application Type - Public
- Permissions -
token:create
Configuring Basis Theory Elements
Basis Theory Elements is available for the following technologies. Click below for detailed instructions on how to install and configure it.
Creating a Text Element
To safely reveal data back to a customer, we must create an Element that will hold and display the data.
In this example we'll create a Text Element, which can hold any string
value.
- JavaScript
- React
- iOS
- Android
import { BasisTheory } from "@basis-theory/basis-theory-js";
let bt;
let textElement;
(async () => {
bt = await new BasisTheory().init("<API_KEY>", { elements: true });
textElement = bt.createElement("text", {
targetId: "exampleTextElement",
});
// here #exampleTextElement is a DOM selector
// example: <div id="exampleTextElement"></div>
await textElement.mount("#exampleTextElement");
})();
import { useRef } from "react";
import {
BasisTheoryProvider,
TextElement,
useBasisTheory,
} from "@basis-theory/basis-theory-react";
function App() {
const { bt } = useBasisTheory("<API_KEY>", { elements: true });
const textRef = useRef(null);
return (
<BasisTheoryProvider bt={bt}>
<TextElement id="exampleTextElement" ref={textRef} />
</BasisTheoryProvider>
);
}
export default App;
To add a Text Element to your iOS app, you just have to follow these three steps:
1. Drop a UITextField onto your Main.storyboard file and select it.
2. Select TextElementUITextField as the class for the UITextField.
3. Add an outlet for your text field to your ViewController.
import UIKit
import BasisTheoryElements
class ViewController: UIViewController {
@IBOutlet weak var exampleTextElement: TextElementUITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
To add a Text Element to your Android app, you just have to follow these two steps:
1. Add the XML below to your MainActivity layout.
2. Reference your XML element in the MainActivity class.
<com.basistheory.android.view.TextElement
android:id="@+id/example_text_element"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
import com.basistheory.android.view.TextElement
class MainActivity : AppCompatActivity() {
private lateinit var exampleTextElement: TextElement
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
exampleTextElement = findViewById(R.id.exampleTextElement)
}
}
<API_KEY>
with the Public API Key you created in Getting Started.Creating a Session
Next, we'll create a Session. Sessions provide temporary elevated access to your public applications, and we'll will use it to safely authenticate the request to the third party. Add the following code to create a session:
- JavaScript
- React
- iOS
- Android
import { BasisTheory } from "@basis-theory/basis-theory-js";
let bt;
let textElement;
(async () => {
bt = await new BasisTheory().init("<API_KEY>", { elements: true });
textElement = bt.createElement("text", {
targetId: "exampleTextElement",
});
// here #exampleTextElement is a DOM selector
// example: <div id="exampleTextElement"></div>
await textElement.mount("#exampleTextElement");
// this is just an example method that'll have the full lifecycle for revealing
const reveal = async () => {
const session = await bt.sessions.create();
};
})();
import { useRef } from "react";
import { BasisTheoryProvider, TextElement, useBasisTheory } from "@basis-theory/basis-theory-react";
function App() {
const { bt } = useBasisTheory("<API_KEY>", { elements: true });
const textRef = useRef(null);
// this is just an example method that'll have the full lifecycle for revealing
const reveal = async () => {
const session = await bt.sessions.create();
};
return (
<BasisTheoryProvider bt={bt}>
<TextElement id="exampleTextElement" ref={textRef} />
</BasisTheoryProvider>
);
}
export default App;
import UIKit
import BasisTheoryElements
class ViewController: UIViewController {
@IBOutlet weak var exampleTextElement: TextElementUITextField!
// this is just an example method that'll have the full lifecycle for revealing
func reveal() {
BasisTheoryElements.createSession(apiKey: btPublicKey) { data, error in
let sessionKey = data!.sessionKey!
let nonce = data!.nonce!
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
import com.basistheory.android.view.TextElement
import com.basistheory.android.service.BasisTheoryElements
class MainActivity : AppCompatActivity() {
private lateinit var exampleTextElement: TextElement
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
exampleTextElement = findViewById(R.id.example_text_element)
}
// this is just an example method that'll have the full lifecycle for revealing
private fun reveal() {
val bt = BasisTheoryElements.builder()
.apiKey("<API_KEY>")
.build()
val session = bt.createSession()
}
}
Creating a Pre-Configured Proxy
To retrieve data from a third party URL without touching it, we will create a Pre-Configured Proxy
Creating a Management Application
To create a proxy, you will need a Management Application.
Click here to create a Management Application or login to your Basis Theory account and create a new application with the following settings:
- Name - Create Proxy
- Application Type - Management
- Permissions:
proxy:create
Creating the Proxy in the Backend
In this guide, we'll use Express.js as our backend but docs are available for different technologies.
We'll create a backend.js
file and add the following code to start the Express.js backend and create a proxy to the third party:
const express = require("express");
const { BasisTheory } = require("@basis-theory/basis-theory-js");
const app = express();
const port = 4242;
let bt = await new BasisTheory().init();
app.post("/create-proxy", async (request, response) => {
const proxy = await bt.proxies.create(
{
name: "Proxy to Third Party",
destinationUrl: "http://third_party_url.com",
requestTransform: {
code: `module.exports = async function (req) {
let { args: { body, headers }, bt } = req;
return {
body,
headers: {
...headers,
"Authorization": req.configuration.Credentials
},
}
};`,
},
configuration: {
Credentials: "credential_1234567890",
},
requireAuth: true,
},
{ apiKey: "<MANAGEMENT_API_KEY>" } // management application
);
response.send(proxy);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Notice that we are using a request transform to add the third party credentials to the Authorization
header before making the request to the third party.
These kind of values can be safely stored in the configuration
object and re-use them for every proxy execution.
Start the server with the following command (from the same directory as backend.js
).
node backend.js
You can now use your favorite API client or just run the following curl
command from your terminal:
curl -X POST http://localhost:4242/create-proxy
<MANAGEMENT_API_KEY>
with the Management API Key you created in Creating a Management Application, http://third_party_url.com
with the third party URL and credential_1234567890
with the third party credentials (optionally).key
value returned from the proxy as its going to be used in the next steps.Authorizing a Session
In order to use the session to securely invoke our proxy, we need to authorize it with a Private Application.
Creating a Private Application
First, lets create the Private Application. To do so Click here or login to your Basis Theory account and create a new application with the following settings:
- Name - Invoke Proxy
- Application Type - Private
- Permissions -
token:use
Authorizing In the Backend
Using the Private Application API Key and the nonce
from our session we can grant temporary authorization to invoke the proxy.
const express = require("express");
const { BasisTheory } = require("@basis-theory/basis-theory-js");
const app = express();
const port = 4242;
let bt = await new BasisTheory().init();
app.post("/create-proxy", async (request, response) => {
const proxy = await bt.proxies.create(
{
name: "Proxy to Third Party",
destinationUrl: "http://third_party_url.com",
requestTransform: {
code: `module.exports = async function (req) {
let { args: { body, headers }, bt } = req;
return {
body,
headers: {
...headers,
"Authorization": req.configuration.Credentials
},
}
};`,
},
configuration: {
Credentials: "credential_1234567890",
},
requireAuth: true,
},
{ apiKey: "<MANAGEMENT_API_KEY>" } // management application
);
response.send(proxy);
});
app.post("/authorize", async (request, response) => {
const { nonce } = request.body;
// authorizing a session returns an empty 200 response
await bt.sessions.authorize(
{
nonce: nonce,
permissions: ["token:use"],
},
{ apiKey: "<PRIVATE_API_KEY>" }
); // private application
// this response is arbitrary and not required
response.json({
result: "success",
});
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
<PRIVATE_API_KEY>
with the Private API Key you created in the Creating a Private Application step.Calling the Authorization Endpoint
Next, from our client application, we'll call the authorization endpoint created on the Authorizing in the Backend step, passing our created session nonce
:
- JavaScript
- React
- iOS
- Android
import { BasisTheory } from "@basis-theory/basis-theory-js";
let bt;
let textElement;
(async () => {
bt = await new BasisTheory().init("<API_KEY>", { elements: true });
textElement = bt.createElement("text", {
targetId: "exampleTextElement",
});
// here #exampleTextElement is a DOM selector
// example: <div id="exampleTextElement"></div>
await textElement.mount("#exampleTextElement");
// this is just an example method that'll have the full lifecycle for revealing
const reveal = async () => {
const session = await bt.sessions.create();
await fetch("http://localhost:4242/authorize", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ nonce: session.nonce }),
});
};
})();
import { useRef } from "react";
import { BasisTheoryProvider, TextElement, useBasisTheory } from "@basis-theory/basis-theory-react";
function App() {
const { bt } = useBasisTheory("<API_KEY>", { elements: true });
const textRef = useRef(null);
// this is just an example method that'll have the full lifecycle for revealing
const reveal = async () => {
const session = await bt.sessions.create();
await fetch("http://localhost:4242/authorize", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ nonce: session.nonce }),
});
};
return (
<BasisTheoryProvider bt={bt}>
<TextElement id="exampleTextElement" ref={textRef} />
</BasisTheoryProvider>
);
}
export default App;
import UIKit
import BasisTheoryElements
class ViewController: UIViewController {
@IBOutlet weak var exampleTextElement: TextElementUITextField!
// this is just an example method that'll have the full lifecycle for revealing
func reveal() {
BasisTheoryElements.createSession(apiKey: btPublicKey) { data, error in
let sessionKey = data!.sessionKey!
let nonce = data!.nonce!
self.authorizeSession(nonce: nonce) { result, error in
//
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func authorizeSession(nonce: String, completion: @escaping ([String: Any]?, Error?) -> Void) {
let parameters = ["nonce": nonce]
let url = URL(string: "http://localhost:4242/authorize")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
completion(nil, error)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]
completion(json, nil)
} catch let error {
completion(nil, error)
}
})
task.resume()
}
}
import com.basistheory.android.view.TextElement
import com.basistheory.android.service.BasisTheoryElements
import java.net.HttpURLConnection
import java.net.URL
class MainActivity : AppCompatActivity() {
private lateinit var exampleTextElement: TextElement
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
exampleTextElement = findViewById(R.id.example_text_element)
}
// this is just an example method that'll have the full lifecycle for revealing
private fun reveal() {
val bt = BasisTheoryElements.builder()
.apiKey("<API_KEY>")
.build()
val session = bt.createSession()
authorizeSession(session.sessionKey)
}
private fun authorizeSession(nonce: String) {
val url = URL("http://localhost:4242/authorize")
val con = url.openConnection() as HttpURLConnection
con.requestMethod = "POST"
con.doOutput = true
con.setRequestProperty("Content-Type", "application/json")
con.setRequestProperty("Accept", "application/json");
val body = String.format("{\"nonce\": \"%s\"}", nonce)
con.outputStream.use { os ->
val input: ByteArray = body.toByteArray(Charsets.UTF_8)
os.write(input, 0, input.size)
}
if (con.responseCode == 200) {
return
}
}
}
Invoking the Proxy and Revealing the Data
With the authorized session, we can now use the sessionKey
to invoke our created proxy.
We'll add the following code to retrieve the data and set its value to the text element.
- JavaScript
- React
- iOS
- Android
import { BasisTheory } from "@basis-theory/basis-theory-js";
let bt;
let textElement;
(async () => {
bt = await new BasisTheory().init("<API_KEY>", { elements: true });
textElement = bt.createElement("text", {
targetId: "exampleTextElement",
});
// here #exampleTextElement is a DOM selector
// example: <div id="exampleTextElement"></div>
await textElement.mount("#exampleTextElement");
// this is just an example method that'll have the full lifecycle for revealing
const reveal = async () => {
const session = await bt.sessions.create();
await fetch("http://localhost:4242/authorize", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ nonce: session.nonce }),
});
const data = await bt.proxy.get({
headers: {
"BT-PROXY-KEY": "proxy_key_1234567890",
},
apiKey: session.sessionKey,
});
textElement.setValue(data);
};
})();
import { useRef } from "react";
import { BasisTheoryProvider, TextElement, useBasisTheory } from "@basis-theory/basis-theory-react";
function App() {
const { bt } = useBasisTheory("<API_KEY>", { elements: true });
const textRef = useRef(null);
// this is just an example method that'll have the full lifecycle for revealing
const reveal = async () => {
const session = await bt.sessions.create();
await fetch("http://localhost:4242/authorize", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ nonce: session.nonce }),
});
const data = await bt.proxy.get({
headers: {
"BT-PROXY-KEY": "proxy_key_1234567890",
},
apiKey: session.sessionKey,
});
textElement.setValue(data);
};
return (
<BasisTheoryProvider bt={bt}>
<TextElement id="exampleTextElement" ref={textRef} />
</BasisTheoryProvider>
);
}
export default App;
import UIKit
import BasisTheoryElements
class ViewController: UIViewController {
@IBOutlet weak var exampleTextElement: TextElementUITextField!
// this is just an example method that'll have the full lifecycle for revealing
func reveal() {
BasisTheoryElements.createSession(apiKey: btPublicKey) { data, error in
let sessionKey = data!.sessionKey!
let nonce = data!.nonce!
self.authorizeSession(nonce: nonce) { result, error in
let proxyHttpRequest = ProxyHttpRequest(method: .get, path: String("/" + issuerCardId))
BasisTheoryElements.proxy(apiKey: sessionKey, proxyKey: "proxy_key_1234567890", proxyHttpRequest: proxyHttpRequest)
{ response, data, error in
DispatchQueue.main.async {
self.cardVerificationCodeElement.setValue(elementValueReference: data!.cvv!.elementValueReference)
}
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func authorizeSession(nonce: String, completion: @escaping ([String: Any]?, Error?) -> Void) {
let parameters = ["nonce": nonce]
let url = URL(string: "http://localhost:4242/authorize")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
completion(nil, error)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request, completionHandler: { data, response, error in
guard error == nil else {
completion(nil, error)
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]
completion(json, nil)
} catch let error {
completion(nil, error)
}
})
task.resume()
}
}
import com.basistheory.android.view.TextElement
import com.basistheory.android.service.BasisTheoryElements
import java.net.HttpURLConnection
import java.net.URL
import com.basistheory.android.service.ProxyRequest
class MainActivity : AppCompatActivity() {
private lateinit var exampleTextElement: TextElement
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
exampleTextElement = findViewById(R.id.example_text_element)
}
// this is just an example method that'll have the full lifecycle for revealing
private fun reveal() {
val bt = BasisTheoryElements.builder()
.apiKey("<API_KEY>")
.build()
val session = bt.createSession()
authorizeSession(session.nonce)
val proxyRequest: ProxyRequest = ProxyRequest().apply {
headers = mapOf(
"BT-PROXY-KEY" to "proxy_key_1234567890",
"Content-Type" to "application/json"
)
}
val data = bt.proxy.get(proxyRequest, session.sessionKey)
exampleTextElement.setValueRef(data)
}
private fun authorizeSession(nonce: String) {
val url = URL("http://localhost:4242/authorize")
val con = url.openConnection() as HttpURLConnection
con.requestMethod = "POST"
con.doOutput = true
con.setRequestProperty("Content-Type", "application/json")
con.setRequestProperty("Accept", "application/json");
val body = String.format("{\"nonce\": \"%s\"}", nonce)
con.outputStream.use { os ->
val input: ByteArray = body.toByteArray(Charsets.UTF_8)
os.write(input, 0, input.size)
}
if (con.responseCode == 200) {
return
}
}
}
🎉 The code above is the last bit that we need to reveal data from a third party!
data
you receive as response from the proxy is not the raw data, but a synthetic reference to it that can only be used with setValue
to display it.Conclusion
You can now reveal any data to a customer without your application accessing the underlying value, reducing compliance and regulatory scope.
Just execute the reveal
method in whichever way you desire (like with the click of a button) and watch the response value from the proxy appear on your Text Element.