in Modules/token-path.js [186:426]
function requestJWTs(event, callback) {
//Retrieving the Authorization Request based on the device code provided by the client application
var DynamoDBParams = {
Key: {
"Device_code": {
S: event.queryStringParameters.device_code
}
},
TableName: process.env.DYNAMODB_TABLE
};
common.dynamodb.getItem(DynamoDBParams, function(err, data) {
if (err) {
//There was an error
console.log("Error occured while retrieving device code");
console.log(err, err.stack);
common.returnJSONError(500, callback);
} else {
//Sucessful
console.log("Sucessful request");
//If Result Set has no value
if (data.Item.length == 0) {
console.log("No item matching device code exists");
common.returnExpiredDeviceCodeError(callback);
//If Result Set has more than one value
} else if (data.Item.length > 1) {
console.log("More than one device code has been returned");
common.returnExpiredDeviceCodeError(callback);
//If Result Set has only one value but it is with an "Expired" status
} else if (data.Item.Status.S == "expired") {
console.log("The Device code has already expired");
common.returnExpiredDeviceCodeError(callback);
//If Result Set has only one value, is not explicitely expired, but has not been requested initally by the same client application
} else if (data.Item.Client_id.S != event.queryStringParameters.client_id) {
console.log("The Client id does not match the initial requestor client id");
common.returnExpiredDeviceCodeError(callback);
//If Result Set has only one value, is not explicitely expired, has been requested initally by the same client application, but has a lifetime older than the maximum lifetime
} else if (Date.now() > parseInt(data.Item.Max_expiry.S)) {
//Update status of the Authorization request to "Expired"
console.log("The Device code has expired");
DynamoDBParams = {
ExpressionAttributeNames: {
"#Status": "Status"
},
ExpressionAttributeValues: {
":status": {
S: "expired"
}
},
Key: {
"Device_code": {
S: event.queryStringParameters.device_code
}
},
ReturnValues: "ALL_NEW",
TableName: process.env.DYNAMODB_TABLE,
UpdateExpression: "SET #Status = :status"
};
common.dynamodb.updateItem(DynamoDBParams, function(err, data) {
if (err) {
//There was an error, so return an JSON error message the Code has expired
console.log("The Device code has expired, but an error occured when updating the DB");
console.log(err, err.stack);
common.returnExpiredDeviceCodeError(callback);
} else {
//Return an JSON error message the Code has expired
common.returnExpiredDeviceCodeError(callback);
}
});
//If Result Set has only one value, is not expired, has been requested initally by the same client application, but application client request a status too quickly
} else if (Date.now() <= (parseInt(data.Item.Last_checked.S) + parseInt(process.env.POLLING_INTERVAL) * 1000) ) {
//Update last checked timestamp of the Authorization request to Now
DynamoDBParams = {
ExpressionAttributeNames: {
"#LC": "Last_checked"
},
ExpressionAttributeValues: {
":lc": {
S: (Date.now()).toString()
}
},
Key: {
"Device_code": {
S: event.queryStringParameters.device_code
}
},
ReturnValues: "ALL_NEW",
TableName: process.env.DYNAMODB_TABLE,
UpdateExpression: "SET #LC = :lc"
};
common.dynamodb.updateItem(DynamoDBParams, function(err, data) {
if (err) {
//There was an error, so return an JSON error message the client application has to slow down
console.log("Client makes too much API calls, but an error occured while updated the last check timestamp in the DB");
console.log(err, err.stack);
common.returnSlowDownError(callback);
} else {
//Return an JSON error message the client application has to slow down
console.log("Client makes too much API calls");
common.returnSlowDownError(callback);
}
});
//If all is good
} else {
//Must check the status
//But first update last checked timestamp of the Authorization request to Now
DynamoDBParams = {
ExpressionAttributeNames: {
"#LC": "Last_checked"
},
ExpressionAttributeValues: {
":lc": {
S: (Date.now()).toString()
}
},
Key: {
"Device_code": {
S: event.queryStringParameters.device_code
}
},
ReturnValues: "ALL_NEW",
TableName: process.env.DYNAMODB_TABLE,
UpdateExpression: "SET #LC = :lc"
};
common.dynamodb.updateItem(DynamoDBParams, function(err, data) {
if (err) {
//There was an error, so return an JSON error message the client application has to slow down
console.log("Client is on time for checking, but an error occured while updated the last check timestamp in the DB");
console.log(err, err.stack);
common.returnSlowDownError(callback);
}
else {
//Sucessfull
console.log("Client is on time for checking, we got a status");
//If the Status is authorization_pending or denied, return the status to the Client application
if (data.Attributes.Status.S == 'authorization_pending' || data.Attributes.Status.S == 'denied') {
console.log("Client is on time for checking, we got a status: " + data.Attributes.Status.S);
common.returnJSONErrorWithMsg(400, data.Attributes.Status.S, callback);
//If the Status is authorized
} else if (data.Attributes.Status.S == 'authorized') {
console.log("Client is on time for checking, we got a status: " + data.Attributes.Status.S);
console.log("Token Set is empty");
//Prepare the retrieving of JWT tokens from Cognito using the Athorization Code grant flow with PKCE
var options = {
hostname: process.env.CUP_DOMAIN + ".auth." + process.env.CUP_REGION + ".amazoncognito.com",
port: 443,
path: '/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
};
//If client application is private, has a Client Secret, and had provided it in the initial request, add it as an Authorization header to this request
if (event.headers.authorization != undefined) {
console.log("Setting Authorization header");
// Client knows authentication is required for Private Client, has been issued a Client secret, and therefore present an authentication header
// Otherwise Client knows authentication is not necessary for Public Client or has made an error
options.headers.authorization = event.headers.authorization;
}
console.log("Launching Request for Tokens");
//Request JWT tokens
const req = https.request(options, res => {
console.log('statusCode:', res.statusCode);
//Reading request's response data
res.on('data', (d) => {
//Prepare JWT Tokens blob
if (d.error) {
console.log("Cognito User Pool returned an error");
common.returnExpiredDeviceCodeError(callback);
} else {
var result = JSON.parse(d.toString());
var response = {}
var rts = process.env.RESULT_TOKEN_SET.split('+');
for (token_type in rts) {
if (rts[token_type] == 'ID') response.id_token = result.id_token;
if (rts[token_type] == 'ACCESS') response.access_token = result.access_token;
if (rts[token_type] == 'REFRESH') response.refresh_token = result.refresh_token;
}
response.expires_in = result.expires_in;
//Update the status of the Authorization request to "Denied" to prevent replay
DynamoDBParams = {
ExpressionAttributeNames: {
"#Status": "Status"
},
ExpressionAttributeValues: {
":status": {
S: "expired"
}
},
Key: {
"Device_code": {
S: event.queryStringParameters.device_code
}
},
ReturnValues: "ALL_NEW",
TableName: process.env.DYNAMODB_TABLE,
UpdateExpression: "SET #Status = :status"
};
common.dynamodb.updateItem(DynamoDBParams, function(err, data) {
if (err) {
//There was an error, return expired message as Authroization code has been used
console.log("We got the tokens but we got an error updating the DB");
console.log(err, err.stack);
common.returnExpiredDeviceCodeError(callback);
} else {
//Return the JWT tokens
common.returnJSONSuccess(response, callback);
}
});
}
});
});
//There was an error retrieving JWT Tokens
req.on('error', (e) => {
console.log("Got an error");
console.log(e);
common.returnExpiredDeviceCodeError(callback);
});
//Writing Body of the request
req.write('grant_type=authorization_code&client_id=' + data.Attributes.Client_id.S + '&scope=' + data.Attributes.Scope.S + '&redirect_uri=' + encodeURIComponent("https://" + process.env.CODE_VERIFICATION_URI + '/callback') + '&code=' + data.Attributes.AuthZ_code.S + '&code_verifier=' + data.Attributes.AuthZ_Verifier_code.S);
//When request is finalized
req.end((e) => {
console.log("Finished");
});
//If Status is not suppoted
} else {
common.returnExpiredDeviceCodeError(callback);
}
}
});
}
}
});
}