Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 70 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -873,18 +873,84 @@ const options = {

Embed context values into a bearer token during generation so you can reference those values in your policies. This enables more flexible access controls, such as tracking end-user identity when making API calls using service accounts, and facilitates using signed data tokens during detokenization.

Generate bearer tokens containing context information using a service account with the context_id identifier. Context information is represented as a JWT claim in a Skyflow-generated bearer token. Tokens generated from such service accounts include a context_identifier claim, are valid for 60 minutes, and can be used to make API calls to the Data and Management APIs, depending on the service account's permissions.
Generate bearer tokens containing context information using a service account with the `context_id` identifier. Context information is represented as a JWT claim in a Skyflow-generated bearer token. Tokens generated from such service accounts include a `context_identifier` claim, are valid for 60 minutes, and can be used to make API calls to the Data and Management APIs, depending on the service account's permissions.

The `ctx` parameter accepts either a **string** or a **JSON object**:

**String context** — use when your policy references a single context value:

```typescript
const options = {
ctx: 'user_12345',
};
const response = await generateBearerToken(filepath, options);
```

**JSON object context** — use when your policy needs multiple context values for conditional data access. Each key in the `ctx` object maps to a Skyflow CEL policy variable under `request.context.*`:

```typescript
const options = {
ctx: {
role: 'admin',
department: 'finance',
user_id: 'user_12345',
},
};
const response = await generateBearerToken(filepath, options);
```

With the object above, your Skyflow policies can reference `request.context.role`, `request.context.department`, and `request.context.user_id` to make conditional access decisions.

You can also set the `context` field on credentials for automatic token generation:

```typescript
// String context on credentials
const credentials: PathCredentials = {
path: 'path/to/credentials.json',
context: 'user_12345',
};

// JSON object context on credentials
const credentials: PathCredentials = {
path: 'path/to/credentials.json',
context: {
role: 'admin',
department: 'finance',
},
};
```

> [!TIP]
> See the full example in the samples directory: [token-generation-with-context-example.ts](samples/service-account/token-generation-with-context-example.ts)
> See [docs.skyflow.com](https://docs.skyflow.com) for more details on authentication, access control, and governance for Skyflow.
> See the full example in the samples directory: [token-generation-with-context-example.ts](samples/service-account/token-generation-with-context-example.ts)
> See Skyflow's [context-aware authorization](https://docs.skyflow.com) and [conditional data access](https://docs.skyflow.com) docs for policy variable syntax like `request.context.*`.

#### Generate signed data tokens: `generateSignedDataTokens(filepath, options)`

Digitally sign data tokens with a service account's private key to add an extra layer of protection. Skyflow generates data tokens when sensitive data is inserted into the vault. Detokenize signed tokens only by providing the signed data token along with a bearer token generated from the service account's credentials. The service account must have the necessary permissions and context to successfully detokenize the signed data tokens.

The `ctx` parameter on signed data tokens also accepts either a **string** or a **JSON object**, using the same format as bearer tokens:

```typescript
// String context
const options = {
ctx: 'user_12345',
dataTokens: ['dataToken1', 'dataToken2'],
timeToLive: 90,
};

// JSON object context
const options = {
ctx: {
role: 'analyst',
department: 'research',
},
dataTokens: ['dataToken1', 'dataToken2'],
timeToLive: 90,
};
```

> [!TIP]
> See the full example in the samples directory: [signed-token-generation-example.ts](samples/service-account/signed-token-generation-example.ts)
> See the full example in the samples directory: [signed-token-generation-example.ts](samples/service-account/signed-token-generation-example.ts)
> See [docs.skyflow.com](https://docs.skyflow.com) for more details on authentication, access control, and governance for Skyflow.

## Logging
Expand Down
50 changes: 42 additions & 8 deletions samples/service-account/signed-token-generation-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ let cred = {
privateKey: '<YOUR_PEM_PRIVATE_KEY>',
};

function getSignedTokenFromFilePath() {
// Approach 1: Signed data tokens with string context
function getSignedTokenWithStringContext() {
return new Promise(async (resolve, reject) => {
try {
const options = {
ctx: 'ctx',
ctx: 'user_12345',
dataTokens: ['dataToken1', 'dataToken2'],
timeToLive: 90 // In seconds.
};
Expand All @@ -33,6 +34,30 @@ function getSignedTokenFromFilePath() {
});
}

// Approach 2: Signed data tokens with JSON object context
// Each key in the ctx object maps to a Skyflow CEL policy variable under request.context.*
// For example: request.context.role == "analyst" && request.context.department == "research"
function getSignedTokenWithObjectContext() {
return new Promise(async (resolve, reject) => {
try {
const options = {
ctx: {
role: 'analyst',
department: 'research',
user_id: 'user_67890',
},
dataTokens: ['dataToken1', 'dataToken2'],
timeToLive: 90, // In seconds.
};
let response = await generateSignedDataTokens(filepath, options);
resolve(response);
} catch (e) {
reject(e);
}
});
}

// Approach 3: Signed data tokens from credentials string
function getSignedTokenFromCreds() {
return new Promise(async (resolve, reject) => {
try {
Expand All @@ -54,16 +79,25 @@ function getSignedTokenFromCreds() {

const tokens = async () => {
try {
const tokenResponseFromFilePath: any = await getSignedTokenFromFilePath();
tokenResponseFromFilePath.forEach((response) => {
console.log(`Data Token: ${response.token}`);
console.log(`Signed Data Token: ${response.signedToken}`);
const tokenResponseString: any = await getSignedTokenWithStringContext();
console.log('Signed tokens (string context):');
tokenResponseString.forEach((response) => {
console.log(` Data Token: ${response.token}`);
console.log(` Signed Data Token: ${response.signedToken}`);
});

const tokenResponseObject: any = await getSignedTokenWithObjectContext();
console.log('Signed tokens (object context):');
tokenResponseObject.forEach((response) => {
console.log(` Data Token: ${response.token}`);
console.log(` Signed Data Token: ${response.signedToken}`);
});

const tokenResponseFromCreds: any = await getSignedTokenFromCreds();
console.log('Signed tokens (from creds):');
tokenResponseFromCreds.forEach((response) => {
console.log(`Data Token: ${response.token}`);
console.log(`Signed Data Token: ${response.signedToken}`);
console.log(` Data Token: ${response.token}`);
console.log(` Signed Data Token: ${response.signedToken}`);
});
} catch (error) {
console.log(error);
Expand Down
45 changes: 41 additions & 4 deletions samples/service-account/token-generation-with-context-example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,46 @@ const cred = {
privateKey: '<YOUR_PEM_PRIVATE_KEY>',
};

function getSkyflowBearerTokenWithContextFromFilePath() {
// Approach 1: Bearer token with string context
// Use a simple string identifier when your policy references a single context value.
// In your Skyflow policy, reference this as: request.context
function getSkyflowBearerTokenWithStringContext() {
return new Promise((resolve, reject) => {
try {
const options = {
ctx: 'context_id',
ctx: 'user_12345',
};
if (!isExpired(bearerToken)) resolve(bearerToken);
else {
generateBearerToken(filepath, options)
.then(response => {
bearerToken = response.accessToken;
resolve(bearerToken);
})
.catch(error => {
reject(error);
});
}
} catch (e) {
reject(e);
}
});
}

// Approach 2: Bearer token with JSON object context
// Use a structured object when your policy needs multiple context values.
// Each key maps to a Skyflow CEL policy variable under request.context.*
// For example, the object below enables policies like:
// request.context.role == "admin" && request.context.department == "finance"
function getSkyflowBearerTokenWithObjectContext() {
return new Promise((resolve, reject) => {
try {
const options = {
ctx: {
role: 'admin',
department: 'finance',
user_id: 'user_12345',
},
};
if (!isExpired(bearerToken)) resolve(bearerToken);
else {
Expand All @@ -42,6 +77,7 @@ function getSkyflowBearerTokenWithContextFromFilePath() {
});
}

// Approach 3: Bearer token with string context from credentials string
function getSkyflowBearerTokenWithContextFromCreds() {
return new Promise((resolve, reject) => {
try {
Expand All @@ -66,8 +102,9 @@ function getSkyflowBearerTokenWithContextFromCreds() {
}

const tokens = async () => {
console.log(await getSkyflowBearerTokenWithContextFromFilePath());
console.log(await getSkyflowBearerTokenWithContextFromCreds());
console.log('String context:', await getSkyflowBearerTokenWithStringContext());
console.log('Object context:', await getSkyflowBearerTokenWithObjectContext());
console.log('Creds string context:', await getSkyflowBearerTokenWithContextFromCreds());
};

tokens();
Loading