Skip to content

Commit

Permalink
session resolver authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
rwieruch committed Apr 16, 2020
1 parent c9adeaa commit 52d9f99
Show file tree
Hide file tree
Showing 13 changed files with 72 additions and 47 deletions.
4 changes: 2 additions & 2 deletions pages/api/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { ServerRequest, ServerResponse } from '@typeDefs/server';
import { ResolverContext } from '@typeDefs/resolver';

import resolvers from '@api/resolvers';
import meMiddleware from '@api/middleware/me';
import sentryMiddleware from '@api/middleware/sentry';
import meMiddleware from '@api/middleware/global/me';
import sentryMiddleware from '@api/middleware/global/sentry';

import firebaseAdmin from '@services/firebase/admin';

Expand Down
3 changes: 2 additions & 1 deletion src/api/authorization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export default shield({
partnerPayments: and(isAuthenticated, isPartner),
},
Mutation: {
passwordChange: isAuthenticated,
// passwordChange: isAuthenticated,
// emailChange: isAuthenticated,
communityJoin: isAuthenticated,

// Admin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default async (
const { session } = context.req.cookies;

if (!session) {
return undefined;
return await resolve(root, args, context, info);
}

const CHECK_REVOKED = true;
Expand Down
File renamed without changes.
15 changes: 15 additions & 0 deletions src/api/middleware/resolver/isAuthenticated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { MiddlewareFn } from 'type-graphql';
import { ForbiddenError } from 'apollo-server';

import { ResolverContext } from '@typeDefs/resolver';

export const isAuthenticated: MiddlewareFn<ResolverContext> = async (
{ context },
next
) => {
if (!context.me) {
return new ForbiddenError('Not authenticated as user.');
}

return next();
};
64 changes: 38 additions & 26 deletions src/api/resolvers/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Ctx,
Resolver,
Mutation,
UseMiddleware,
} from 'type-graphql';

import { ResolverContext } from '@typeDefs/resolver';
Expand All @@ -13,11 +14,12 @@ import firebase from '@services/firebase/client';
import firebaseAdmin from '@services/firebase/admin';
import { inviteToRevue } from '@services/revue';
import { inviteToConvertkit } from '@services/convertkit';
import { isAuthenticated } from '@api/middleware/resolver/isAuthenticated';

@ObjectType()
class SessionToken {
@Field()
sessionToken: string;
token: string;
}

@Resolver()
Expand All @@ -26,28 +28,37 @@ export default class SessionResolver {
async signIn(
@Arg('email') email: string,
@Arg('password') password: string
) {
): Promise<SessionToken> {
let result;

try {
result = await firebase
.auth()
.signInWithEmailAndPassword(email, password);
} catch (error) {
return new Error(error);
throw new Error(error);
}

if (!result.user) {
throw new Error('No user found.');
}

const idToken = await result.user?.getIdToken();
const sessionToken = await firebaseAdmin
const idToken = await result.user.getIdToken();

const token = await firebaseAdmin
.auth()
.createSessionCookie(idToken || '', {
.createSessionCookie(idToken, {
expiresIn: EXPIRES_IN,
});

if (!token) {
throw new Error('Not able to create a session cookie.');
}

// We manage the session ourselves.
await firebase.auth().signOut();

return { sessionToken };
return { token };
}

@Mutation(() => SessionToken)
Expand All @@ -63,23 +74,22 @@ export default class SessionResolver {
displayName: username,
});
} catch (error) {
if (error.message.includes('email address is already in use')) {
return new Error(
'You already registered with this email. Hint: Check your password manager for our old domain: roadtoreact.com'
);
} else {
return new Error(error);
}
throw new Error(error);
}

const { user } = await firebase
.auth()
.signInWithEmailAndPassword(email, password);

const idToken = await user?.getIdToken();
const sessionToken = await firebaseAdmin
if (!user) {
throw new Error('No user found.');
}

const idToken = await user.getIdToken();

const token = await firebaseAdmin
.auth()
.createSessionCookie(idToken || '', {
.createSessionCookie(idToken, {
expiresIn: EXPIRES_IN,
});

Expand All @@ -98,47 +108,49 @@ export default class SessionResolver {
console.log(error);
}

return { sessionToken };
return { token };
}

@Mutation(() => Boolean, { nullable: true })
@Mutation(() => Boolean)
async passwordForgot(@Arg('email') email: string) {
try {
await firebase.auth().sendPasswordResetEmail(email);
} catch (error) {
return new Error(error);
throw new Error(error);
}

return true;
}

@Mutation(() => Boolean, { nullable: true })
@Mutation(() => Boolean)
@UseMiddleware(isAuthenticated)
async passwordChange(
@Arg('password') password: string,
@Ctx() ctx: ResolverContext
) {
try {
await firebaseAdmin.auth().updateUser(ctx.me?.uid || '', {
await firebaseAdmin.auth().updateUser(ctx.me!.uid, {
password,
});
} catch (error) {
return new Error(error);
throw new Error(error);
}

return true;
}

@Mutation(() => Boolean, { nullable: true })
@Mutation(() => Boolean)
@UseMiddleware(isAuthenticated)
async emailChange(
@Arg('email') email: string,
@Ctx() ctx: ResolverContext
) {
try {
await firebaseAdmin.auth().updateUser(ctx.me?.uid || '', {
await firebaseAdmin.auth().updateUser(ctx.me!.uid, {
email,
});
} catch (error) {
return new Error(error);
throw new Error(error);
}

return true;
Expand Down
16 changes: 8 additions & 8 deletions src/generated/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ export type Mutation = {
migrate: Scalars['Boolean'];
signIn: SessionToken;
signUp: SessionToken;
passwordForgot?: Maybe<Scalars['Boolean']>;
passwordChange?: Maybe<Scalars['Boolean']>;
emailChange?: Maybe<Scalars['Boolean']>;
passwordForgot: Scalars['Boolean'];
passwordChange: Scalars['Boolean'];
emailChange: Scalars['Boolean'];
paypalCreateOrder: PaypalOrderId;
paypalApproveOrder: Scalars['Boolean'];
stripeCreateOrder: StripeId;
Expand Down Expand Up @@ -347,7 +347,7 @@ export type QueryPartnerSalesArgs = {

export type SessionToken = {
__typename?: 'SessionToken';
sessionToken: Scalars['String'];
token: Scalars['String'];
};

export type StorefrontBundle = {
Expand Down Expand Up @@ -682,7 +682,7 @@ export type SignUpMutation = (
{ __typename?: 'Mutation' }
& { signUp: (
{ __typename?: 'SessionToken' }
& Pick<SessionToken, 'sessionToken'>
& Pick<SessionToken, 'token'>
) }
);

Expand All @@ -696,7 +696,7 @@ export type SignInMutation = (
{ __typename?: 'Mutation' }
& { signIn: (
{ __typename?: 'SessionToken' }
& Pick<SessionToken, 'sessionToken'>
& Pick<SessionToken, 'token'>
) }
);

Expand Down Expand Up @@ -1435,7 +1435,7 @@ export type PaypalApproveOrderMutationOptions = ApolloReactCommon.BaseMutationOp
export const SignUpDocument = gql`
mutation SignUp($username: String!, $email: String!, $password: String!) {
signUp(username: $username, email: $email, password: $password) {
sessionToken
token
}
}
`;
Expand Down Expand Up @@ -1469,7 +1469,7 @@ export type SignUpMutationOptions = ApolloReactCommon.BaseMutationOptions<SignUp
export const SignInDocument = gql`
mutation SignIn($email: String!, $password: String!) {
signIn(email: $email, password: $password) {
sessionToken
token
}
}
`;
Expand Down
4 changes: 2 additions & 2 deletions src/queries/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ export const SIGN_UP = gql`
$password: String!
) {
signUp(username: $username, email: $email, password: $password) {
sessionToken
token
}
}
`;

export const SIGN_IN = gql`
mutation SignIn($email: String!, $password: String!) {
signIn(email: $email, password: $password) {
sessionToken
token
}
}
`;
Expand Down
3 changes: 0 additions & 3 deletions src/screens/PasswordChange/PasswordChangeForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ const PasswordChangeForm = ({ form }: PasswordChangeFormProps) => {
const { successMessage } = useIndicators({
key: 'password-change',
error,
success: {
message: 'Success! Check your email inbox.',
},
});

const [
Expand Down
2 changes: 1 addition & 1 deletion src/screens/SignIn/SignInForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const SignInForm = ({
},
});

cookie.set('session', data?.signIn.sessionToken || '', {
cookie.set('session', data?.signIn.token || '', {
expires: EXPIRES_IN,
// TODO: 1) Get it work with httpOnly 2) Get it work on the server. See SignUpForm.tsx
// httpOnly: true,
Expand Down
2 changes: 1 addition & 1 deletion src/screens/SignIn/SignInForm/spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('SignInForm', () => {
},
result: () => {
mutationCalled = true;
return { data: { signIn: { sessionToken: '1' } } };
return { data: { signIn: { token: '1' } } };
},
},
];
Expand Down
2 changes: 1 addition & 1 deletion src/screens/SignUp/SignUpForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const SignUpForm = ({
},
});

cookie.set('session', data?.signUp.sessionToken || '', {
cookie.set('session', data?.signUp.token || '', {
expires: EXPIRES_IN,
// TODO: 1) Get it work with httpOnly 2) Get it work on the server. See SignUpForm.tsx
// httpOnly: true,
Expand Down
2 changes: 1 addition & 1 deletion src/screens/SignUp/SignUpForm/spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe('SignUpForm', () => {
},
result: () => {
mutationCalled = true;
return { data: { signUp: { sessionToken: '1' } } };
return { data: { signUp: { token: '1' } } };
},
},
];
Expand Down

0 comments on commit 52d9f99

Please sign in to comment.