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

Tnramalho feature/GitHub adjustments #227

Merged
merged 6 commits into from
Aug 20, 2024
Merged
Changes from 1 commit
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
Next Next commit
chore: improvements on github
  • Loading branch information
tnramalho committed Aug 20, 2024
commit da77bbca71365807ded7a85fb893247ba6a86240
23 changes: 14 additions & 9 deletions packages/nestjs-auth-github/src/auth-github.strategy.ts
Original file line number Diff line number Diff line change
@@ -2,11 +2,6 @@ import { Strategy } from 'passport-github';
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';

import {
ReferenceEmailInterface,
ReferenceIdInterface,
} from '@concepta/ts-core';

import {
FederatedOAuthService,
FederatedCredentialsInterface,
@@ -18,6 +13,9 @@ import {
} from './auth-github.constants';

import { AuthGithubSettingsInterface } from './interfaces/auth-github-settings.interface';
import { AuthGithubProfileInterface } from './interfaces/auth-github-profile.interface';
import { AuthGithubMissingEmailException } from './exceptions/auth-github-missing-email.exception';
import { AuthGithubMissingIdException } from './exceptions/auth-github-missing-id.exception';

@Injectable()
export class AuthGithubStrategy extends PassportStrategy(
@@ -26,7 +24,7 @@ export class AuthGithubStrategy extends PassportStrategy(
) {
constructor(
@Inject(AUTH_GITHUB_MODULE_SETTINGS_TOKEN)
settings: AuthGithubSettingsInterface,
private settings: AuthGithubSettingsInterface,
private federatedOAuthService: FederatedOAuthService,
) {
super({
@@ -39,15 +37,22 @@ export class AuthGithubStrategy extends PassportStrategy(
async validate(
_accessToken: string,
_refreshToken: string,
profile: ReferenceIdInterface & ReferenceEmailInterface,
profile: AuthGithubProfileInterface & Record<string, string>,
): Promise<FederatedCredentialsInterface> {
// TODO: should we save accessToken and refreshToken?

const gitProfile =
this.settings.profileFormatter && this.settings.profileFormatter(profile);

if (!gitProfile?.id) throw new AuthGithubMissingIdException();

if (!gitProfile?.email) throw new AuthGithubMissingEmailException();

// Create a new user if it doesn't exist or just return based on federated
const user = await this.federatedOAuthService.sign(
AUTH_GITHUB_STRATEGY_NAME,
profile.email,
profile.id,
gitProfile.email,
gitProfile.id,
);

if (!user) {
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import { registerAs } from '@nestjs/config';
import { AUTH_GITHUB_MODULE_DEFAULT_SETTINGS_TOKEN } from '../auth-github.constants';
import { AuthGithubSettingsInterface } from '../interfaces/auth-github-settings.interface';
import { AuthGithubLoginDto } from '../dto/auth-github-login.dto';
import { profileFormatter } from '../utils';

/**
* Default configuration for auth github.
@@ -16,5 +17,6 @@ export const authGithubDefaultConfig = registerAs(
clientId: process.env.GITHUB_CLIENT_ID ?? 'client_id',
clientSecret: process.env.GITHUB_CLIENT_SECRET ?? 'secret',
callbackURL: process.env.GITHUB_CALLBACK_URL ?? 'callback_url',
profileFormatter,
}),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ExceptionInterface } from '@concepta/ts-core';

export class AuthGithubMissingEmailException
extends Error
implements ExceptionInterface
{
errorCode = 'AUTH_GITHUB_MISSING_PROFILE_EMAIL_ERROR';

constructor(
message = 'GitHub did not return an email address for the user.',
) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ExceptionInterface } from '@concepta/ts-core';

export class AuthGithubMissingIdException
extends Error
implements ExceptionInterface
{
errorCode = 'AUTH_GITHUB_MISSING_PROFILE_ID_ERROR';

constructor(message = 'GitHub did not return an id for the user.') {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface AuthGithubEmailsInterface {
value: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ReferenceIdInterface } from '@concepta/ts-core';
import { AuthGithubEmailsInterface } from './auth-github-emails.interface';

export interface AuthGithubProfileInterface extends ReferenceIdInterface {
displayName?: string;
username?: string;
email?: string;
emails?: AuthGithubEmailsInterface[];
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { AuthenticationCodeInterface } from '@concepta/ts-common';
import { Type } from '@nestjs/common';
import { AuthGithubProfileInterface } from './auth-github-profile.interface';
import { AuthGithubSignInterface } from './auth-github-sign.interface';

export interface AuthGithubSettingsInterface {
clientId: string;
clientSecret: string;
callbackURL: string;
loginDto?: Type<AuthenticationCodeInterface>;
profileFormatter: (
profile: AuthGithubProfileInterface,
) => AuthGithubSignInterface;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ReferenceIdInterface } from '@concepta/ts-core';

export interface AuthGithubSignInterface extends ReferenceIdInterface {
email: string;
}
19 changes: 19 additions & 0 deletions packages/nestjs-auth-github/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { AuthGithubProfileInterface } from '../interfaces/auth-github-profile.interface';
import { AuthGithubSignInterface } from '../interfaces/auth-github-sign.interface';

export const profileFormatter = (
profile: AuthGithubProfileInterface,
): AuthGithubSignInterface => {
let email = '';

if (profile.email) email = profile.email;
else if (profile.emails && profile.emails.length > 0) {
email = profile.emails[0].value;
}

const result: AuthGithubSignInterface = {
id: profile?.id || '',
email,
};
return result;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { format } from 'util';
import { ExceptionInterface } from '@concepta/ts-core';

export class FederatedUserRelationshipException
extends Error
implements ExceptionInterface
{
errorCode = 'FEDERATED_USER_RELATIONSHIP_ERROR';

context: {
entityName: string;
federatedId: string;
};

constructor(
entityName: string,
federatedId: string,
message = 'Error while trying to load user relationship from federated $s',
) {
super(format(message, federatedId));
this.context = {
entityName,
federatedId,
};
}
}
Original file line number Diff line number Diff line change
@@ -59,6 +59,12 @@ export class FederatedOAuthService implements FederatedOAuthServiceInterface {
queryOptions,
);
} else {
if (!federated.user?.id)
throw new FederatedUserLookupException(
this.constructor.name,
federated.user,
);

const user = await this.userLookupService.byId(
federated.user.id,
queryOptions,
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ export class FederatedService
provider,
subject,
},
relations: ['user'],
});
} catch (e) {
const exception = e instanceof Error ? e : new NotAnErrorException(e);