Develop a client
This guide explains how to implement an Alias client to fetch user's data.
First, you'll need to generate a contract which describes who you are, what data you want to access and why. A contract is built as follow:
const Anychain = require('@alias/anychain');
const chain = new Anychain();
// generate a secret key
const secretSeed = chain.seed();
// derive signature key
const signSk = chain.signSeedKeypair(chain.seedOf(secretSeed, 32, "sign"));
// generate a client declaration
const client = {
type: "alias.client.decl",
// Application's name
name: "Example App",
// Application's description
desc: "First example Alias client",
// Application's Web domain
domain: "client.alias",
// Company legally responsible for the application
company: "FooBar Inc",
};
client.crypto = { sign: signSk.publicKey };
const signedClient = chain.sign(signSk, client);
const contract = {
type: "alias.contract",
// link to the signed client declaration
client: signedClient,
// list of legal bases: 'contractual', 'consent' & 'legitimate'
base: {
// Data required under the contractual legal base are required by the
// client to perform the contract. Read more below.
contractual: {
// list of scopes
scopes: [
{
provider: "foo",
path: "bar",
},
{
provider: "foo",
path: "qux"
}
],
// list of usages of how this data will be used
usages: [
"Contractual description of how this data will be used",
]
},
// Data required under the consent legal base are optional and opt-out;
consent: [
// List of consent data, each with a different usage explaination
{
// list of scopes
scopes: [
{
provider: "foo",
path: "bar",
},
{
provider: "foo",
path: "qux"
}
],
// list of usages of how this data will be used
usages: [
"Consent description of how this data will be used",
]
},
...
],
// Legitimate XXX
legitimate: {
// global reason why a legitimate request is performed
reason: "Global description of why legitimate data are required",
// List of legitimate requests
groups: [
{
// list of scopes
scopes: [
{
provider: "foo",
path: "bar",
},
{
provider: "foo",
path: "qux"
}
],
// list of usages of how this data will be used
usages: [
"Legitimate description of how this data will be used",
]
}
],
}
},
// List of legal metadata necessary to be fully compliant with GDPR/CCPA/...
// legislation. See below for more detailled explaination of what each value
// means.
legal: {
accept_users_right_access_modify_transfer_delete: false,
automated_decision: false,
automated_surveillance: false,
destination: [
"Destination #1",
"Destination #2",
...
],
email_dpo: "dpo@client.alias",
evaluation_notation_rating_profiling: false,
innovative_reasonable_expectations: false,
mixing_reasonable_expectations: false,
storage_duration: "1 year after revocation",
subprocessors: [],
tos_url: "https://client.alias/tos/",
transfer_outside_eea: false,
},
network: {
// optional. Protocol scheme. By default, 'https'.
// in production, an authorization server MUST reject any contract which
// scheme is not 'https'.
scheme: 'https',
// optional. Redirection endpoint after the authorization server
// receives the agreement or denial of the user about a contract. By
// default, '/alias/cb'.
redirectEndpoint: '/alias/cb',
// optional. Push endpoint where the granted data will be uploaded. By
// default, '/alias/push/'
pushEndpoint: '/alias/push',
}
};
This contract (stored in variable contract
) may be written into a file in its
Anychain's token form.
fs.writeFileSync("contract.token", chain.toToken(contract));
We develop an Web UI to generate a contract. You just need to fill the form and
you'll be able at the end of the process to generate the file contract.token
.
Link to the contract generator
To identity a user, you'll need its alias. The client is responsible to get the user's alias, which will redirect the user-agent to the user's authorization server.
To request the user for access to its data, use the method Alias.request
.
// in the HTML file, add in the tag <head>:
// <script type="application/javascript" src="https://cdn.gdpr.dev/anychain.js"></script>
// <script type="application/javascript" src="https://cdn.gdpr.dev/alias-client.js"></script>
const alias = "john.doe@gdpr.dev"
const contract = chain.await $.ajax("contract.json")
Alias.request(alias, contract)
.catch((e) => {
// on error
alert("Impossible to log in with your alias: " + e);
})
// on success, will redirect the user to its authorization server.
After the user-agent's redirection, the authorization server will call back the
client when the user made a decision, with a grant code or an error message. By
default, the call back is a GET
HTTP request to the redirection endpoint.
If the user denied or an error occured, the URL parameter error
is set to one
of the following values:
-
access_defined
: The user denied the contract.
If the user agreed, the URL parameter code
is set to the grant token. This
should be persistently stored linked to your account of the user. It will be
used to request your user's data.
You'll need to run an Alias data server which will receive the granted data. The Alias data server is packaged as a Docker image.
First get the project and build it. Then, do
make build-docker
Now the image is ready, start an Alias data server.
docker run -v ./data:/data -p 8080:80 -p 3000:3000 alias/data-server
- The port 8080 serves HTTP requests from the authorization server, which should be reverse-proxy from the app's web domain.
- The port 3000 serves an HTTP REST API to let internal services fetch user's data.
3000 is a private port and should be accessed only by services which need to access the user's data. It MUST NOT be made public as it doesn't provide any credential mechanism, and anyone with a grant token would be able to access user's data.
The folder ./data
will store the received granted data.
The autorization server will push the data to the client by performing HTTP
requests on the client's Web domain (contract's field client.body.domain
) and
on endpoints whose base is /alias/push
(or overwritten with contract's field
network.pushEndpoint
).
The traffic to the push endpoint should be reverse-proxy to the Alias client server.
# for nginx.
# SSL reverse-proxy
server {
listen 443 ssl;
listen [::]:443 ssl;
...
# add the following to your nginx configuration
location ^~ /alias {
proxy_pass http://$upstream:8080;
}
}
A internal service can request any data from any valid grant token. To do so,
perform a GET
HTTP request on the /api/grant/%grant_hash%/path/to/file
,
where grant_hash
is the root hash of the grant token.
const Anychain = require('@alias/anychain');
const chain = new Anychain();
// path of the file or folder to access
const path = "/path/to/file";
// build the file's URL
const grant = chain.fromToken(grantToken);
const grantHash = chain.fold(grant);
const url = `http://$upstream:3000/api/grant/${grantHash.base64()}${path}`;
fetch(url);
If the path is a file, the content of the file will be returned. If the path is a folder, a JSON array with every (recursive) children will be returned:
{status: 'ok', 'resources': ['/foo', '/bar/quz']}
If the data has not been received yet, the request will block and the Alias data server will request the authorization server to process the grant. If something's wrong, the Alias data server will return one of the following status:
- HTTP status code
502
: the authorization server is down. The transfer is aborted. Retry later. - HTTP status code
503
: the transfer is happening but is taking too long to hold the connection opened. The client should retry after some time. - HTTP status code
504
: the authorization server initiated a transfer but timed out. The transfer is aborted. Retry later.
- more info about the legal contract