-
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
311 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import type { Config } from '@jest/types'; | ||
|
||
const config: Config.InitialOptions = { | ||
preset: 'ts-jest', | ||
collectCoverageFrom: ['src/**/*.ts'], | ||
coverageReporters: ['lcov', 'text-summary'], | ||
setupFilesAfterEnv: ['jest-matcher-specific-error'], | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
{ | ||
"name": "@logto/node", | ||
"version": "1.0.0-alpha.2", | ||
"source": "./src/index.ts", | ||
"main": "./lib/index.js", | ||
"exports": { | ||
"require": "./lib/index.js", | ||
"import": "./lib/module.js" | ||
}, | ||
"module": "./lib/module.js", | ||
"types": "./lib/index.d.ts", | ||
"files": [ | ||
"lib" | ||
], | ||
"license": "MIT", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/logto-io/js.git", | ||
"directory": "packages/node" | ||
}, | ||
"scripts": { | ||
"dev:tsc": "tsc -p tsconfig.build.json -w --preserveWatchOutput", | ||
"precommit": "lint-staged", | ||
"check": "tsc --noEmit", | ||
"build": "rm -rf lib/ && pnpm check && parcel build", | ||
"lint": "eslint --ext .ts src", | ||
"test": "jest", | ||
"test:coverage": "jest --silent --coverage", | ||
"prepack": "pnpm test" | ||
}, | ||
"dependencies": { | ||
"@logto/client": "^1.0.0-alpha.2", | ||
"@silverhand/essentials": "^1.1.6", | ||
"js-base64": "^3.7.2", | ||
"node-fetch": "^2.6.7" | ||
}, | ||
"devDependencies": { | ||
"@jest/types": "^27.5.1", | ||
"@parcel/core": "^2.6.2", | ||
"@parcel/packager-ts": "^2.6.2", | ||
"@parcel/transformer-typescript-types": "^2.6.2", | ||
"@silverhand/eslint-config": "^0.14.0", | ||
"@silverhand/ts-config": "^0.14.0", | ||
"@types/jest": "^27.4.0", | ||
"eslint": "^8.9.0", | ||
"jest": "^27.5.1", | ||
"jest-location-mock": "^1.0.9", | ||
"jest-matcher-specific-error": "^1.0.0", | ||
"lint-staged": "^13.0.0", | ||
"parcel": "^2.6.2", | ||
"prettier": "^2.3.2", | ||
"ts-jest": "^27.0.4", | ||
"typescript": "^4.5.5" | ||
}, | ||
"eslintConfig": { | ||
"extends": "@silverhand" | ||
}, | ||
"prettier": "@silverhand/eslint-config/.prettierrc", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"targets": { | ||
"main": { | ||
"context": "node", | ||
"includeNodeModules": false | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
declare module 'node-fetch' { | ||
const foo: typeof fetch; | ||
export = foo; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import LogtoClient from '.'; | ||
|
||
const appId = 'app_id_value'; | ||
const endpoint = 'https://logto.dev'; | ||
|
||
const navigate = jest.fn(); | ||
const storage = { | ||
setItem: jest.fn(), | ||
getItem: jest.fn(), | ||
removeItem: jest.fn(), | ||
}; | ||
|
||
describe('LogtoClient', () => { | ||
describe('constructor', () => { | ||
it('constructor should not throw', () => { | ||
expect(() => new LogtoClient({ endpoint, appId }, { navigate, storage })).not.toThrow(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import BaseClient, { LogtoConfig, createRequester, ClientAdapter } from '@logto/client'; | ||
import fetch from 'node-fetch'; | ||
|
||
import { generateCodeChallenge, generateCodeVerifier, generateState } from './utils/generators'; | ||
|
||
export type { | ||
IdTokenClaims, | ||
LogtoErrorCode, | ||
LogtoConfig, | ||
LogtoClientErrorCode, | ||
} from '@logto/client'; | ||
export { LogtoError, OidcError, Prompt, LogtoRequestError, LogtoClientError } from '@logto/client'; | ||
|
||
export default class LogtoClient extends BaseClient { | ||
constructor(config: LogtoConfig, adapter: Pick<ClientAdapter, 'navigate' | 'storage'>) { | ||
super(config, { | ||
...adapter, | ||
requester: createRequester(fetch), | ||
generateCodeChallenge, | ||
generateCodeVerifier, | ||
generateState, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { UrlSafeBase64 } from '@silverhand/essentials'; | ||
import { toUint8Array } from 'js-base64'; | ||
|
||
import { generateCodeChallenge, generateCodeVerifier, generateState } from './generators'; | ||
|
||
describe('generateState', () => { | ||
test('should be random value', () => { | ||
const state1 = generateState(); | ||
const state2 = generateState(); | ||
expect(state1).not.toEqual(state2); | ||
}); | ||
|
||
test('should be url-safe', () => { | ||
const state = generateState(); | ||
expect(UrlSafeBase64.isSafe(state)).toBeTruthy(); | ||
}); | ||
|
||
test('raw random data length should be length 64', () => { | ||
const state = generateState(); | ||
expect(toUint8Array(state).length).toEqual(64); | ||
}); | ||
}); | ||
|
||
describe('generateCodeVerifier', () => { | ||
test('should be random value', () => { | ||
const codeVerifier1 = generateCodeVerifier(); | ||
const codeVerifier2 = generateCodeVerifier(); | ||
expect(codeVerifier1).not.toEqual(codeVerifier2); | ||
}); | ||
|
||
test('should be url-safe', () => { | ||
const codeVerifier = generateCodeVerifier(); | ||
expect(UrlSafeBase64.isSafe(codeVerifier)).toBeTruthy(); | ||
}); | ||
|
||
test('raw random data length should be length 64', () => { | ||
const codeVerifier = generateCodeVerifier(); | ||
expect(toUint8Array(codeVerifier).length).toEqual(64); | ||
}); | ||
}); | ||
|
||
describe('generateCodeChallenge', () => { | ||
test('dealing with different code verifiers should not be equal', async () => { | ||
const codeVerifier1 = generateCodeVerifier(); | ||
const codeChallenge1 = await generateCodeChallenge(codeVerifier1); | ||
const codeVerifier2 = generateCodeVerifier(); | ||
const codeChallenge2 = await generateCodeChallenge(codeVerifier2); | ||
expect(codeChallenge1).not.toEqual(codeChallenge2); | ||
}); | ||
|
||
test('dealing with same code verifier should be equal', async () => { | ||
const codeVerifier = generateCodeVerifier(); | ||
const codeChallenge1 = await generateCodeChallenge(codeVerifier); | ||
const codeChallenge2 = await generateCodeChallenge(codeVerifier); | ||
expect(codeChallenge1).toEqual(codeChallenge2); | ||
}); | ||
|
||
describe('dealing with static code verifier should not throw', () => { | ||
test('dealing with url-safe code verifier should not throw', async () => { | ||
const codeVerifier = | ||
'tO6MabnMFRAatnlMa1DdSstypzzkgalL1-k8Hr_GdfTj-VXGiEACqAkSkDhFuAuD8FOU8lMishaXjt29Xt2Oww'; | ||
const codeChallenge = await generateCodeChallenge(codeVerifier); | ||
expect(codeChallenge).toEqual('0K3SLeGlNNzFswYJjcVzcN4C76m_8NZORxFJLBJWGwg'); | ||
}); | ||
|
||
describe('dealing with non-url-safe code verifier should not throw', () => { | ||
test('latin1 character', async () => { | ||
const codeVerifier = 'Á'; | ||
const codeChallenge = await generateCodeChallenge(codeVerifier); | ||
expect(codeChallenge).toEqual('p3yvZiKYauPicLIDZ0W1peDz4Z9KFC-9uxtDfoO1KOQ'); | ||
}); | ||
|
||
test('emoji character', async () => { | ||
const codeVerifier = '🚀'; | ||
const codeChallenge = await generateCodeChallenge(codeVerifier); | ||
expect(codeChallenge).toEqual('67wLKHDrMj8rbP-lxJPO74GufrNq_HPU4DZzAWMdrsU'); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** @link [Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636) */ | ||
import { randomFillSync, createHash } from 'crypto'; | ||
|
||
import { fromUint8Array } from 'js-base64'; | ||
|
||
/** | ||
* @param length The length of the raw random data. | ||
*/ | ||
const generateRandomString = (length = 64) => | ||
fromUint8Array(randomFillSync(new Uint8Array(length)), true); | ||
|
||
/** | ||
* Generates random string for state and encodes them in url safe base64 | ||
*/ | ||
export const generateState = () => generateRandomString(); | ||
|
||
/** | ||
* Generates code verifier | ||
* | ||
* @link [Client Creates a Code Verifier](https://datatracker.ietf.org/doc/html/rfc7636#section-4.1) | ||
*/ | ||
export const generateCodeVerifier = () => generateRandomString(); | ||
|
||
/** | ||
* Calculates the S256 PKCE code challenge for an arbitrary code verifier and encodes it in url safe base64 | ||
* | ||
* @param {String} codeVerifier Code verifier to calculate the S256 code challenge for | ||
* @link [Client Creates the Code Challenge](https://datatracker.ietf.org/doc/html/rfc7636#section-4.2) | ||
*/ | ||
export const generateCodeChallenge = async (codeVerifier: string): Promise<string> => { | ||
const encodedCodeVerifier = new TextEncoder().encode(codeVerifier); | ||
const hash = createHash('sha256'); | ||
hash.update(encodedCodeVerifier); | ||
const codeChallenge = hash.digest(); | ||
|
||
return fromUint8Array(codeChallenge, true); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"extends": "@silverhand/ts-config/tsconfig.base", | ||
"compilerOptions": { | ||
"outDir": "lib", | ||
"types": [ | ||
"jest", | ||
"jest-matcher-specific-error" | ||
] | ||
}, | ||
"include": [ | ||
"src", | ||
"jest.config.ts", | ||
] | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.