Skip to content

Commit 4715887

Browse files
committed
Update tests for fine-grained ACLs
1 parent 405d06b commit 4715887

File tree

6 files changed

+544
-12
lines changed

6 files changed

+544
-12
lines changed

package-lock.json

Lines changed: 102 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,16 @@
2929
"url": "https://github.com/adobe/da-admin"
3030
},
3131
"license": "Apache-2.0",
32+
"mocha": {
33+
"require": [
34+
"nock"
35+
]
36+
},
3237
"dependencies": {
3338
"@aws-sdk/client-s3": "^3.456.0",
3439
"@aws-sdk/s3-request-presigner": "^3.468.0",
3540
"@ssttevee/cfw-formdata-polyfill": "^0.2.1",
36-
"jose": "^5.1.3"
41+
"jose": "^5.9.6"
3742
},
3843
"devDependencies": {
3944
"@adobe/eslint-config-helix": "2.0.6",
@@ -52,6 +57,7 @@
5257
"husky": "^9.1.7",
5358
"lint-staged": "^15.4.3",
5459
"mocha": "^10.2.0",
60+
"nock": "^14.0.1",
5561
"semantic-release": "^24.2.2",
5662
"wrangler": "^3.107.3"
5763
},

src/utils/auth.js

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12-
import { decodeJwt } from 'jose';
12+
import { createRemoteJWKSet, jwtVerify, jwksCache } from 'jose';
1313

1414
export async function logout({ daCtx, env }) {
1515
await Promise.all(daCtx.users.map((u) => env.DA_AUTH.delete(u.ident)));
@@ -53,14 +53,77 @@ export async function setUser(userId, expiration, headers, env) {
5353
return value;
5454
}
5555

56+
/**
57+
* Retrieve cached IMS keys from KV Store
58+
* @param {*} env
59+
* @param {string} keysUrl
60+
* @returns {Promise<import('jose').ExportedJWKSCache>}
61+
*/
62+
async function getPreviouslyCachedJWKS(env, keysUrl) {
63+
const cachedJwks = await env.DA_AUTH.get(keysUrl);
64+
if (!cachedJwks) return {};
65+
return JSON.parse(cachedJwks);
66+
}
67+
68+
/**
69+
* Store new set of IMS keys in the KV Store
70+
* @param {*} env
71+
* @param {string} keysUrl
72+
* @param {import('jose').ExportedJWKSCache} keysCache
73+
* @returns {Promise<void>}
74+
*/
75+
async function storeJWSInCache(env, keysUrl, keysCache) {
76+
try {
77+
await env.DA_AUTH.put(
78+
keysUrl,
79+
JSON.stringify(keysCache),
80+
{
81+
expirationTtl: 24 * 60 * 60, // 24 hours in seconds
82+
},
83+
);
84+
} catch (err) {
85+
// An error may be thrown if a write to the same key is made within 1 second
86+
// eslint-disable-next-line no-console
87+
console.error('Failed to store keys in cache', err);
88+
}
89+
}
90+
5691
export async function getUsers(req, env) {
5792
const authHeader = req.headers?.get('authorization');
5893
if (!authHeader) return [{ email: 'anonymous' }];
5994

6095
async function parseUser(token) {
6196
if (!token || token.trim().length === 0) return { email: 'anonymous' };
6297

63-
const { user_id: userId, created_at: createdAt, expires_in: expiresIn } = decodeJwt(token);
98+
let payload;
99+
try {
100+
const keysURL = `${env.IMS_ORIGIN}/ims/keys`;
101+
102+
const keysCache = await getPreviouslyCachedJWKS(env, keysURL);
103+
const { uat } = keysCache;
104+
105+
const jwks = createRemoteJWKSet(
106+
new URL(keysURL),
107+
{
108+
[jwksCache]: keysCache,
109+
cacheMaxAge: 24 * 60 * 60 * 1000, // 24 hours in milliseconds
110+
},
111+
);
112+
113+
({ payload } = await jwtVerify(token, jwks));
114+
115+
if (uat !== keysCache.uat) {
116+
await storeJWSInCache(env, keysURL, keysCache);
117+
}
118+
} catch (e) {
119+
// eslint-disable-next-line no-console
120+
console.log('IMS token offline verification failed', e);
121+
return { email: 'anonymous' };
122+
}
123+
124+
if (!payload) return { email: 'anonymous' };
125+
126+
const { user_id: userId, created_at: createdAt, expires_in: expiresIn } = payload;
64127
const expires = Number(createdAt) + Number(expiresIn);
65128
const now = Math.floor(new Date().getTime() / 1000);
66129

test/utils/mocks/env.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const env = {
2525
S3_DEF_URL: 'https://s3.com',
2626
S3_ACCESS_KEY_ID: 'an-id',
2727
S3_SECRET_ACCESS_KEY: 'too-many-secrets',
28+
IMS_ORIGIN: 'https://ims-na1.adobelogin.com',
2829
DA_AUTH: {
2930
get: (kvNamespace) => {
3031
return NAMESPACES[kvNamespace];

test/utils/mocks/jose.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12-
const decodeJwt = (token) => {
12+
const jwtVerify = (token) => {
1313
let [email, created_at = 0, expires_in = 0] = token.split(':');
1414
created_at += Math.floor(new Date().getTime() / 1000);
1515
expires_in += created_at;
1616
return {
17-
user_id: email,
18-
created_at,
19-
expires_in: expires_in || created_at + 1000,
17+
payload: {
18+
user_id: email,
19+
created_at,
20+
expires_in: expires_in || created_at + 1000,
21+
},
2022
};
2123
};
2224

23-
export default { decodeJwt };
25+
export default { jwtVerify };

0 commit comments

Comments
 (0)