diff --git a/package-lock.json b/package-lock.json index c38eb1d..a71ee09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2142,8 +2142,8 @@ "dev": true }, "node_modules/@icynet/oauth2-provider": { - "version": "1.0.0", - "resolved": "git+https://gitlab.icynet.eu/IcyNetwork/oauth2-provider.git#a440d1f4ac53ccb6989dd25221797490611e240a", + "version": "1.0.5", + "resolved": "git+https://gitlab.icynet.eu/IcyNetwork/oauth2-provider.git#20c0771bf08cafb27809e6cdb21dbff0ae40aec3", "license": "MIT", "dependencies": { "express": "^4.17.3", @@ -14155,7 +14155,7 @@ "dev": true }, "@icynet/oauth2-provider": { - "version": "git+https://gitlab.icynet.eu/IcyNetwork/oauth2-provider.git#a440d1f4ac53ccb6989dd25221797490611e240a", + "version": "git+https://gitlab.icynet.eu/IcyNetwork/oauth2-provider.git#20c0771bf08cafb27809e6cdb21dbff0ae40aec3", "from": "@icynet/oauth2-provider@git+https://gitlab.icynet.eu/IcyNetwork/oauth2-provider.git", "requires": { "express": "^4.17.3", diff --git a/src/modules/api/admin/admin.service.ts b/src/modules/api/admin/admin.service.ts index 7f9a76d..cb74c84 100644 --- a/src/modules/api/admin/admin.service.ts +++ b/src/modules/api/admin/admin.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { Client } from 'connect-redis'; import { User } from 'src/modules/objects/user/user.entity'; -const UNPRIVILEGED_STRIP = ['openid', 'id_token', 'management', 'implicit']; +const UNPRIVILEGED_STRIP = ['id_token', 'management', 'implicit']; @Injectable() export class AdminService { diff --git a/src/modules/config/config.providers.ts b/src/modules/config/config.providers.ts index 49059b0..f9a05fa 100644 --- a/src/modules/config/config.providers.ts +++ b/src/modules/config/config.providers.ts @@ -39,8 +39,8 @@ export const configProviders: Provider[] = [ }, jwt: { algorithm: 'RS256', - issuer: 'localhost', - expiration: 3600, + issuer: 'http://localhost', + expiration: 604800, }, database: { type: 'mysql', diff --git a/src/modules/oauth2/adapter/client.adapter.ts b/src/modules/oauth2/adapter/client.adapter.ts index 71f07ba..76fe432 100644 --- a/src/modules/oauth2/adapter/client.adapter.ts +++ b/src/modules/oauth2/adapter/client.adapter.ts @@ -56,6 +56,8 @@ export class ClientAdapter implements OAuth2ClientAdapter { } checkScope(client: OAuth2Client, scope: string[]): boolean { - return scope.every((one) => client.scope.includes(one)); + return scope.every( + (one) => one === 'profile' || client.scope.includes(one), + ); } } diff --git a/src/modules/oauth2/adapter/code.adapter.ts b/src/modules/oauth2/adapter/code.adapter.ts index 85401c0..4d04493 100644 --- a/src/modules/oauth2/adapter/code.adapter.ts +++ b/src/modules/oauth2/adapter/code.adapter.ts @@ -12,6 +12,7 @@ export class CodeAdapter implements OAuth2CodeAdapter { clientId: string, scope: string | string[], ttl: number, + nonce?: string, ): Promise { const client = await this._service.clientService.getById(clientId); const user = await this._service.userService.getById(userId); @@ -31,6 +32,7 @@ export class CodeAdapter implements OAuth2CodeAdapter { scopes, expiresAt, user, + nonce, ); return accessToken; diff --git a/src/modules/oauth2/adapter/jwt.adapter.ts b/src/modules/oauth2/adapter/jwt.adapter.ts new file mode 100644 index 0000000..8a9346d --- /dev/null +++ b/src/modules/oauth2/adapter/jwt.adapter.ts @@ -0,0 +1,43 @@ +import { JWTAdapter, OAuth2User, OAuth2Client } from '@icynet/oauth2-provider'; +import { OAuth2Service } from '../oauth2.service'; + +export class IcyJWTAdapter implements JWTAdapter { + constructor(private _client: OAuth2Service) {} + + async issueIdToken( + rawUser: OAuth2User, + rawClient: OAuth2Client, + scope: string[], + nonce?: string, + ): Promise { + const user = await this._client.userService.getById(rawUser.id as number); + + const userData: Record = { + name: user.display_name, + preferred_username: user.username, + nickname: user.display_name, + updated_at: user.updated_at, + nonce, + }; + + if (scope.includes('email')) { + userData.email = user.email; + userData.email_verified = true; + } + + if ( + (scope.includes('image') || scope.includes('profile')) && + user.picture + ) { + userData.picture = `${this._client.config.get('app.base_url')}/uploads/${ + user.picture.file + }`; + } + + return this._client.jwt.issue( + userData, + user.uuid as string, + rawClient.id as string, + ); + } +} diff --git a/src/modules/oauth2/oauth2.module.ts b/src/modules/oauth2/oauth2.module.ts index 3f0bca1..1e3d59c 100644 --- a/src/modules/oauth2/oauth2.module.ts +++ b/src/modules/oauth2/oauth2.module.ts @@ -3,10 +3,17 @@ import { OAuth2ClientModule } from 'src/modules/objects/oauth2-client/oauth2-cli import { OAuth2TokenModule } from 'src/modules/objects/oauth2-token/oauth2-token.module'; import { UploadModule } from 'src/modules/objects/upload/upload.module'; import { UserModule } from 'src/modules/objects/user/user.module'; +import { JWTModule } from '../jwt/jwt.module'; import { OAuth2Service } from './oauth2.service'; @Module({ - imports: [UserModule, UploadModule, OAuth2ClientModule, OAuth2TokenModule], + imports: [ + UserModule, + UploadModule, + OAuth2ClientModule, + OAuth2TokenModule, + JWTModule, + ], providers: [OAuth2Service], exports: [OAuth2Service], }) diff --git a/src/modules/oauth2/oauth2.service.ts b/src/modules/oauth2/oauth2.service.ts index d7e89bc..647e97c 100644 --- a/src/modules/oauth2/oauth2.service.ts +++ b/src/modules/oauth2/oauth2.service.ts @@ -5,9 +5,11 @@ import { OAuth2ClientService } from 'src/modules/objects/oauth2-client/oauth2-cl import { OAuth2TokenService } from 'src/modules/objects/oauth2-token/oauth2-token.service'; import { UserService } from 'src/modules/objects/user/user.service'; import { TokenService } from 'src/modules/utility/services/token.service'; +import { JWTService } from '../jwt/jwt.service'; import { AccessTokenAdapter } from './adapter/access-token.adapter'; import { ClientAdapter } from './adapter/client.adapter'; import { CodeAdapter } from './adapter/code.adapter'; +import { IcyJWTAdapter } from './adapter/jwt.adapter'; import { RefreshTokenAdapter } from './adapter/refresh-token.adapter'; import { UserAdapter } from './adapter/user.adapter'; @@ -27,6 +29,7 @@ export class OAuth2Service { user: new UserAdapter(this), client: new ClientAdapter(this), code: new CodeAdapter(this), + jwt: new IcyJWTAdapter(this), }; public oauth = new OAuth2Provider( @@ -64,6 +67,7 @@ export class OAuth2Service { constructor( public token: TokenService, + public jwt: JWTService, public config: ConfigurationService, public userService: UserService, public clientService: OAuth2ClientService, diff --git a/src/modules/objects/oauth2-client/oauth2-client.entity.ts b/src/modules/objects/oauth2-client/oauth2-client.entity.ts index 6be87d0..5bf2de9 100644 --- a/src/modules/objects/oauth2-client/oauth2-client.entity.ts +++ b/src/modules/objects/oauth2-client/oauth2-client.entity.ts @@ -4,7 +4,6 @@ import { Entity, ManyToOne, OneToMany, - OneToOne, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; diff --git a/src/modules/objects/oauth2-client/oauth2-client.service.ts b/src/modules/objects/oauth2-client/oauth2-client.service.ts index eef0db1..bd25865 100644 --- a/src/modules/objects/oauth2-client/oauth2-client.service.ts +++ b/src/modules/objects/oauth2-client/oauth2-client.service.ts @@ -20,7 +20,13 @@ export class OAuth2ClientService { 'implicit', ]; - public availableScopes = ['image', 'email', 'privileges', 'management']; + public availableScopes = [ + 'image', + 'email', + 'privileges', + 'management', + 'openid', + ]; constructor( @Inject('CLIENT_REPOSITORY') diff --git a/src/modules/objects/oauth2-token/oauth2-token.service.ts b/src/modules/objects/oauth2-token/oauth2-token.service.ts index 520cb8a..5074d50 100644 --- a/src/modules/objects/oauth2-token/oauth2-token.service.ts +++ b/src/modules/objects/oauth2-token/oauth2-token.service.ts @@ -18,6 +18,7 @@ export class OAuth2TokenService { scope: string, expiry: Date, user?: User, + nonce?: string, ): Promise { const newToken = new OAuth2Token(); newToken.client = client; @@ -26,6 +27,7 @@ export class OAuth2TokenService { newToken.scope = scope; newToken.user = user; newToken.expires_at = expiry; + newToken.nonce = nonce; await this.tokenRepository.save(newToken);