Reveal Tokenized Data
This guide will show you how to reveal sensitive data 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
Creating a Token
To reveal tokenized data, you must have a token already created and stored with Basis Theory. If you haven't done it yet, head over to the Collect Data section of our docs for detailed guides on how to safely collect and tokenize data.
Configuring Basis Theory Elements
Basis Theory Elements are available for the following technologies. Click below for detailed instructions on how to install and configure them.
Creating a Text Element
To safely reveal tokenized 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 use it to safely retrieve the data from the token. 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()
}
}
Authorizing a Session
In order to use the session to retrieve data, we need to authorize it with a Private Application.
Creating a Private Application
First, lets create the Private Application. To do so, Login to your Basis Theory account and create a new application with the following settings:
- Name - Reveal Data
- Application Type - Private
- Access Rule (Use the
Advanced Rule Builder
option)- Description - Reveal Data
- Container -
/
- Permissions -
token:read
- Transform -
reveal
Authorizing In the Backend
Using the Private Application API Key, the nonce
from our session, and a Condition we ensure we are only granting authorization for the desired token.
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 authorize an incoming session.
const express = require("express");
const { BasisTheory } = require("@basis-theory/basis-theory-js");
const app = express();
const port = 4242;
app.post("/authorize", async (request, response) => {
const bt = await new BasisTheory().init("<PRIVATE_API_KEY>");
const { nonce } = request.body;
// authorizing a session returns an empty 200 response
await bt.sessions.authorize({
nonce: nonce,
rules: [
{
description: "Reveal Token",
priority: 1,
conditions: [
{
attribute: "id",
operator: "equals",
value: "token_id_1234567890",
},
],
permissions: ["token:read", "token:use"],
transform: "reveal",
},
],
});
// this response is arbitrary and not required
response.json({
result: "success",
});
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Start the server with the following command (from the same directory as backend.js
).
node backend.js
<PRIVATE_API_KEY>
with the Private API Key you created in Creating a Private Application and token_id_1234567890
with the id
for the token you wish to reveal.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.nonce)
}
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
}
}
}
Retrieving and Revealing a Token
With the authorized session, we can now use the sessionKey
to retrieve the token from the Basis Theory backend.
We'll add the following code to retrieve the token 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 token = await bt.tokens.retrieve("token_id_1234567890", {
apiKey: session.sessionKey,
});
textElement.setValue(token.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 token = await bt.tokens.retrieve("token_id_1234567890", {
apiKey: session.sessionKey,
});
textRef.current.setValue(token.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
BasisTheoryElements.getTokenById(id: 'token_id_1234567890', apiKey: sessionKey) { data, error in
self.exampleTextElement.setValue(elementValueReference: data!.data!.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
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 token = bt.getToken("token_id_1234567890", session.sessionKey)
exampleTextElement.setValueRef(token.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 a token!
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 token value appear on your Text Element.
If you need to reveal data stored outside of Basis Theory in a similar manner, check our Reveal Data from 3rd Party guide.