actually implement oauth2 pcke, drop image scope
This commit is contained in:
parent
4d6267d40a
commit
c3c297a9a5
14
package-lock.json
generated
14
package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@icynet/oauth2-provider": "^1.0.6",
|
||||
"@icynet/oauth2-provider": "^1.0.7",
|
||||
"@nestjs/common": "^9.0.11",
|
||||
"@nestjs/core": "^9.0.11",
|
||||
"@nestjs/platform-express": "^9.0.11",
|
||||
@ -2142,9 +2142,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@icynet/oauth2-provider": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@icynet/oauth2-provider/-/oauth2-provider-1.0.6.tgz",
|
||||
"integrity": "sha512-CsPQZB0Jbzxll4re34aPtZFVNkeeWtC4aW9UZCg8U57fPL8/Xe/2dfnigxdn4r9jVdd6d/qiFSh5x7wrFUmYrw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@icynet/oauth2-provider/-/oauth2-provider-1.0.7.tgz",
|
||||
"integrity": "sha512-YdzkB8c/7BOUZaiKpeEFbLfttfH6kztDm+qUG3zqgZ6J+CXJMqtLnJtFu++bn8/okYikU1ErdZq2/4fetD1C+Q==",
|
||||
"dependencies": {
|
||||
"express": "^4.17.3",
|
||||
"express-session": "^1.17.2"
|
||||
@ -14155,9 +14155,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@icynet/oauth2-provider": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@icynet/oauth2-provider/-/oauth2-provider-1.0.6.tgz",
|
||||
"integrity": "sha512-CsPQZB0Jbzxll4re34aPtZFVNkeeWtC4aW9UZCg8U57fPL8/Xe/2dfnigxdn4r9jVdd6d/qiFSh5x7wrFUmYrw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@icynet/oauth2-provider/-/oauth2-provider-1.0.7.tgz",
|
||||
"integrity": "sha512-YdzkB8c/7BOUZaiKpeEFbLfttfH6kztDm+qUG3zqgZ6J+CXJMqtLnJtFu++bn8/okYikU1ErdZq2/4fetD1C+Q==",
|
||||
"requires": {
|
||||
"express": "^4.17.3",
|
||||
"express-session": "^1.17.2"
|
||||
|
@ -24,7 +24,7 @@
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@icynet/oauth2-provider": "^1.0.6",
|
||||
"@icynet/oauth2-provider": "^1.0.7",
|
||||
"@nestjs/common": "^9.0.11",
|
||||
"@nestjs/core": "^9.0.11",
|
||||
"@nestjs/platform-express": "^9.0.11",
|
||||
|
@ -24,7 +24,7 @@ export class AppController {
|
||||
response_types_supported: ['code', 'id_token'],
|
||||
id_token_signing_alg_values_supported: [this.config.get('jwt.algorithm')],
|
||||
subject_types_supported: ['public'],
|
||||
scopes_supported: ['openid', 'profile', 'email'],
|
||||
scopes_supported: ['openid', 'profile', 'picture', 'email'],
|
||||
claims_supported: [
|
||||
'aud',
|
||||
'exp',
|
||||
@ -33,6 +33,7 @@ export class AppController {
|
||||
'sub',
|
||||
'name',
|
||||
'preferred_username',
|
||||
'nickname',
|
||||
'profile',
|
||||
'picture',
|
||||
'updated_at',
|
||||
|
17
src/migration/1670052416869-pcke.ts
Normal file
17
src/migration/1670052416869-pcke.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class pcke1670052416869 implements MigrationInterface {
|
||||
name = 'pcke1670052416869';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`o_auth2_token\` ADD \`pcke\` text NULL`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`o_auth2_token\` DROP COLUMN \`pcke\``,
|
||||
);
|
||||
}
|
||||
}
|
@ -73,16 +73,11 @@ export class ApiController {
|
||||
userData.email_verified = true;
|
||||
}
|
||||
|
||||
if (
|
||||
(scope.includes('image') ||
|
||||
scope.includes('picture') ||
|
||||
scopelessAccess) &&
|
||||
user.picture
|
||||
) {
|
||||
userData.image = `${this._config.get('app.base_url')}/uploads/${
|
||||
if ((scope.includes('picture') || scopelessAccess) && user.picture) {
|
||||
userData.picture = `${this._config.get('app.base_url')}/uploads/${
|
||||
user.picture.file
|
||||
}`;
|
||||
userData.image_file = user.picture.file;
|
||||
userData.picture_file = user.picture.file;
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -6,6 +6,7 @@ export class CodeAdapter implements OAuth2CodeAdapter {
|
||||
constructor(private _service: OAuth2Service) {}
|
||||
|
||||
ttl = 3600;
|
||||
challengeMethods = ['plain', 'S256'];
|
||||
|
||||
async create(
|
||||
userId: number,
|
||||
@ -13,6 +14,8 @@ export class CodeAdapter implements OAuth2CodeAdapter {
|
||||
scope: string | string[],
|
||||
ttl: number,
|
||||
nonce?: string,
|
||||
codeChallenge?: string,
|
||||
codeChallengeMethod?: 'plain' | 'S256',
|
||||
): Promise<string> {
|
||||
const client = await this._service.clientService.getById(clientId);
|
||||
const user = await this._service.userService.getById(userId);
|
||||
@ -24,6 +27,12 @@ export class CodeAdapter implements OAuth2CodeAdapter {
|
||||
).join(' ');
|
||||
|
||||
const expiresAt = new Date(Date.now() + ttl * 1000);
|
||||
const pcke =
|
||||
codeChallenge && codeChallengeMethod
|
||||
? `${this.challengeMethods.indexOf(
|
||||
codeChallengeMethod,
|
||||
)}:${codeChallenge}`
|
||||
: null;
|
||||
|
||||
this._service.tokenService.insertToken(
|
||||
accessToken,
|
||||
@ -33,6 +42,7 @@ export class CodeAdapter implements OAuth2CodeAdapter {
|
||||
expiresAt,
|
||||
user,
|
||||
nonce,
|
||||
pcke,
|
||||
);
|
||||
|
||||
return accessToken;
|
||||
@ -49,11 +59,22 @@ export class CodeAdapter implements OAuth2CodeAdapter {
|
||||
return null;
|
||||
}
|
||||
|
||||
let codeChallenge: string;
|
||||
let codeChallengeMethod: 'plain' | 'S256';
|
||||
if (find.pcke) {
|
||||
codeChallengeMethod = this.challengeMethods[
|
||||
Number(find.pcke.substring(0, 1))
|
||||
] as 'plain' | 'S256';
|
||||
codeChallenge = find.pcke.substring(2);
|
||||
}
|
||||
|
||||
return {
|
||||
...find,
|
||||
code: find.token,
|
||||
client_id: find.client.client_id,
|
||||
user_id: find.user.id,
|
||||
code_challenge: codeChallenge,
|
||||
code_challenge_method: codeChallengeMethod,
|
||||
};
|
||||
}
|
||||
|
||||
@ -82,4 +103,11 @@ export class CodeAdapter implements OAuth2CodeAdapter {
|
||||
checkTTL(code: OAuth2Code): boolean {
|
||||
return code.expires_at.getTime() > Date.now();
|
||||
}
|
||||
|
||||
getCodeChallenge(code: OAuth2Code) {
|
||||
return {
|
||||
method: code.code_challenge_method,
|
||||
challenge: code.code_challenge,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,7 @@ export class IcyJWTAdapter implements JWTAdapter {
|
||||
userData.email_verified = true;
|
||||
}
|
||||
|
||||
if (
|
||||
(scope.includes('image') ||
|
||||
scope.includes('picture') ||
|
||||
scope.includes('profile')) &&
|
||||
user.picture
|
||||
) {
|
||||
if (scope.includes('picture') && user.picture) {
|
||||
userData.picture = `${this._client.config.get('app.base_url')}/uploads/${
|
||||
user.picture.file
|
||||
}`;
|
||||
|
@ -15,7 +15,7 @@ import { UserAdapter } from './adapter/user.adapter';
|
||||
|
||||
const SCOPE_DESCRIPTION: Record<string, string> = {
|
||||
email: 'Email address',
|
||||
image: 'Profile picture',
|
||||
picture: 'Profile picture',
|
||||
};
|
||||
|
||||
const ALWAYS_AVAILABLE = ['Username and display name'];
|
||||
@ -40,10 +40,7 @@ export class OAuth2Service {
|
||||
let disallowedScopes = [...ALWAYS_UNAVAILABLE];
|
||||
|
||||
Object.keys(SCOPE_DESCRIPTION).forEach((item) => {
|
||||
if (
|
||||
scope.includes(item) ||
|
||||
(item === 'image' && scope.includes('picture'))
|
||||
) {
|
||||
if (scope.includes(item)) {
|
||||
allowedScopes.push(SCOPE_DESCRIPTION[item]);
|
||||
} else {
|
||||
disallowedScopes.push(SCOPE_DESCRIPTION[item]);
|
||||
|
@ -21,7 +21,6 @@ export class OAuth2ClientService {
|
||||
];
|
||||
|
||||
public availableScopes = [
|
||||
'image',
|
||||
'picture',
|
||||
'profile',
|
||||
'email',
|
||||
|
@ -29,6 +29,9 @@ export class OAuth2Token {
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
nonce: string;
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
pcke: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
scope: string;
|
||||
|
||||
|
@ -19,6 +19,7 @@ export class OAuth2TokenService {
|
||||
expiry: Date,
|
||||
user?: User,
|
||||
nonce?: string,
|
||||
pcke?: string,
|
||||
): Promise<OAuth2Token> {
|
||||
const newToken = new OAuth2Token();
|
||||
newToken.client = client;
|
||||
@ -28,6 +29,7 @@ export class OAuth2TokenService {
|
||||
newToken.user = user;
|
||||
newToken.expires_at = expiry;
|
||||
newToken.nonce = nonce;
|
||||
newToken.pcke = pcke;
|
||||
|
||||
await this.tokenRepository.save(newToken);
|
||||
|
||||
|
Reference in New Issue
Block a user