Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(authentication,grpc-sdk): ValidateAccessToken gRPC method #729

Merged
merged 3 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libraries/grpc-sdk/src/modules/authentication/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
UserDeleteResponse,
UserLoginResponse,
Team,
ValidateAccessTokenResponse,
} from '../../protoUtils';

export class Authentication extends ConduitModule<typeof AuthenticationDefinition> {
Expand Down Expand Up @@ -45,4 +46,11 @@ export class Authentication extends ConduitModule<typeof AuthenticationDefinitio
teamDelete(teamId: string): Promise<TeamDeleteResponse> {
return this.client!.teamDelete({ teamId });
}

validateAccessToken(
accessToken: string,
path?: string,
): Promise<ValidateAccessTokenResponse> {
return this.client!.validateAccessToken({ accessToken, path });
}
}
38 changes: 38 additions & 0 deletions modules/authentication/src/Authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<Config> {
configSchema = AppConfigSchema;
Expand All @@ -54,6 +58,7 @@ export default class Authentication extends ManagedModule<Config> {
getTeam: this.getTeam.bind(this),
createTeam: this.createTeam.bind(this),
teamDelete: this.teamDelete.bind(this),
validateAccessToken: this.validateAccessToken.bind(this),
},
};
protected metricsSchema = metricsSchema;
Expand Down Expand Up @@ -373,6 +378,39 @@ export default class Authentication extends ManagedModule<Config> {
return callback(null, { message: result as string });
}

async validateAccessToken(
call: GrpcRequest<ValidateAccessTokenRequest>,
callback: GrpcCallback<ValidateAccessTokenResponse>,
) {
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);
Expand Down
17 changes: 17 additions & 0 deletions modules/authentication/src/authentication.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
2 changes: 1 addition & 1 deletion modules/authentication/src/routes/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading