diff --git a/libraries/grpc-sdk/src/modules/authentication/index.ts b/libraries/grpc-sdk/src/modules/authentication/index.ts index e1d7f9269..17fc8b39f 100644 --- a/libraries/grpc-sdk/src/modules/authentication/index.ts +++ b/libraries/grpc-sdk/src/modules/authentication/index.ts @@ -6,6 +6,7 @@ import { UserDeleteResponse, UserLoginResponse, Team, + ValidateAccessTokenResponse, } from '../../protoUtils'; export class Authentication extends ConduitModule { @@ -45,4 +46,11 @@ export class Authentication extends ConduitModule { return this.client!.teamDelete({ teamId }); } + + validateAccessToken( + accessToken: string, + path?: string, + ): Promise { + return this.client!.validateAccessToken({ accessToken, path }); + } } diff --git a/modules/authentication/src/Authentication.ts b/modules/authentication/src/Authentication.ts index ce00b2084..6a6f7dec4 100644 --- a/modules/authentication/src/Authentication.ts +++ b/modules/authentication/src/Authentication.ts @@ -27,6 +27,9 @@ import { GetTeamRequest, CreateTeamRequest, Team as GrpcTeam, + ValidateAccessTokenRequest, + ValidateAccessTokenResponse, + ValidateAccessTokenResponse_Status, } from './protoTypes/authentication'; import { runMigrations } from './migrations'; import metricsSchema from './metrics'; @@ -40,6 +43,7 @@ import { } from '@conduitplatform/module-tools'; import { TeamsAdmin } from './admin/team'; import { User as UserAuthz } from './authz'; +import { handleAuthentication } from './routes/middleware'; export default class Authentication extends ManagedModule { configSchema = AppConfigSchema; @@ -54,6 +58,7 @@ export default class Authentication extends ManagedModule { getTeam: this.getTeam.bind(this), createTeam: this.createTeam.bind(this), teamDelete: this.teamDelete.bind(this), + validateAccessToken: this.validateAccessToken.bind(this), }, }; protected metricsSchema = metricsSchema; @@ -373,6 +378,39 @@ export default class Authentication extends ManagedModule { return callback(null, { message: result as string }); } + async validateAccessToken( + call: GrpcRequest, + callback: GrpcCallback, + ) { + const accessToken = call.request.accessToken; + const path = call.request.path ?? ''; + let userId: string | undefined = undefined; + const accessStatus = await handleAuthentication( + {}, + { authorization: `Bearer ${accessToken}` }, + {}, + path, + ) + .then(r => { + userId = (r.user as models.User)._id; + return ValidateAccessTokenResponse_Status.AUTHENTICATED; + }) + .catch(err => { + switch (err.code as status) { + case status.PERMISSION_DENIED: + return ValidateAccessTokenResponse_Status.USER_BLOCKED; + case status.UNAUTHENTICATED: + if (err.message.includes('2FA')) { + return ValidateAccessTokenResponse_Status.REQUIRES_2FA; + } + // intentional fall through + default: + return ValidateAccessTokenResponse_Status.UNAUTHENTICATED; + } + }); + return callback(null, { status: accessStatus, userId }); + } + protected registerSchemas() { const promises = Object.values(models).map(model => { const modelInstance = model.getInstance(this.database); diff --git a/modules/authentication/src/authentication.proto b/modules/authentication/src/authentication.proto index 4dce145d4..63c543593 100644 --- a/modules/authentication/src/authentication.proto +++ b/modules/authentication/src/authentication.proto @@ -59,6 +59,22 @@ message TeamDeleteResponse { string message = 1; } +message ValidateAccessTokenRequest { + string accessToken = 1; + optional string path = 2; +} + +message ValidateAccessTokenResponse { + enum Status { + AUTHENTICATED = 0; + UNAUTHENTICATED = 1; + REQUIRES_2FA = 2; + USER_BLOCKED = 3; + } + Status status = 1; + optional string userId = 2; +} + service Authentication { rpc UserLogin(UserLoginRequest) returns (UserLoginResponse); rpc UserCreate(UserCreateRequest) returns (UserCreateResponse); @@ -67,4 +83,5 @@ service Authentication { rpc GetTeam(GetTeamRequest) returns (Team); rpc CreateTeam(CreateTeamRequest) returns (Team); rpc TeamDelete(TeamDeleteRequest) returns (TeamDeleteResponse); + rpc ValidateAccessToken(ValidateAccessTokenRequest) returns (ValidateAccessTokenResponse); } diff --git a/modules/authentication/src/routes/middleware.ts b/modules/authentication/src/routes/middleware.ts index 3c8931ef4..9358709b6 100644 --- a/modules/authentication/src/routes/middleware.ts +++ b/modules/authentication/src/routes/middleware.ts @@ -38,7 +38,7 @@ export async function authMiddleware( return handleAuthentication(context, headers, cookies, call.request.path); } -async function handleAuthentication( +export async function handleAuthentication( context: Indexable, headers: Headers, cookies: Cookies,