Skip to content

Commit

Permalink
feat: add auth guard for app routes, handling session expires for gra…
Browse files Browse the repository at this point in the history
…phql
  • Loading branch information
xmlking committed May 12, 2024
1 parent 6ac26c5 commit 5fb331f
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 27 deletions.
67 changes: 64 additions & 3 deletions apps/console-fb/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,34 @@ type mutation_root {
updates: [rules_updates!]!
): [rules_mutation_response]

"""
update data of the table: "user_org_roles"
"""
update_user_org_roles(
"""sets the columns of the filtered rows to the given values"""
_set: user_org_roles_set_input

"""filter the rows which have to be updated"""
where: user_org_roles_bool_exp!
): user_org_roles_mutation_response

"""
update single row of the table: "user_org_roles"
"""
update_user_org_roles_by_pk(
"""sets the columns of the filtered rows to the given values"""
_set: user_org_roles_set_input
pk_columns: user_org_roles_pk_columns_input!
): user_org_roles

"""
update multiples rows of table: "user_org_roles"
"""
update_user_org_roles_many(
"""updates to execute, in order"""
updates: [user_org_roles_updates!]!
): [user_org_roles_mutation_response]

"""
update multiples rows of table: "auth.users"
"""
Expand Down Expand Up @@ -3314,6 +3342,7 @@ type user_org_roles {
createdAt: timestamptz!
createdBy: uuid!
id: uuid!
isDefaultRole: Boolean!
organization: String!
role: String!
userId: uuid!
Expand All @@ -3329,6 +3358,7 @@ input user_org_roles_bool_exp {
createdAt: timestamptz_comparison_exp
createdBy: uuid_comparison_exp
id: uuid_comparison_exp
isDefaultRole: Boolean_comparison_exp
organization: String_comparison_exp
role: String_comparison_exp
userId: uuid_comparison_exp
Expand All @@ -3343,6 +3373,11 @@ enum user_org_roles_constraint {
"""
user_org_roles_pkey

"""
unique or primary key constraint on columns "user_id", "organization"
"""
user_org_roles_user_id_organization_default_role_unique

"""
unique or primary key constraint on columns "user_id", "organization", "role"
"""
Expand All @@ -3353,6 +3388,7 @@ enum user_org_roles_constraint {
input type for inserting data into table "user_org_roles"
"""
input user_org_roles_insert_input {
isDefaultRole: Boolean
organization: String
role: String
userId: uuid
Expand Down Expand Up @@ -3383,11 +3419,17 @@ input user_org_roles_order_by {
createdAt: order_by
createdBy: order_by
id: order_by
isDefaultRole: order_by
organization: order_by
role: order_by
userId: order_by
}

"""primary key columns input for table: user_org_roles"""
input user_org_roles_pk_columns_input {
id: uuid!
}

"""
select columns of table "user_org_roles"
"""
Expand All @@ -3401,6 +3443,9 @@ enum user_org_roles_select_column {
"""column name"""
id

"""column name"""
isDefaultRole

"""column name"""
organization

Expand All @@ -3411,6 +3456,13 @@ enum user_org_roles_select_column {
userId
}

"""
input type for updating data in table "user_org_roles"
"""
input user_org_roles_set_input {
isDefaultRole: Boolean
}

"""
Streaming cursor of the table "user_org_roles"
"""
Expand All @@ -3427,17 +3479,26 @@ input user_org_roles_stream_cursor_value_input {
createdAt: timestamptz
createdBy: uuid
id: uuid
isDefaultRole: Boolean
organization: String
role: String
userId: uuid
}

"""
placeholder for update columns of table "user_org_roles" (current role has no relevant permissions)
update columns of table "user_org_roles"
"""
enum user_org_roles_update_column {
"""placeholder (do not use)"""
_PLACEHOLDER
"""column name"""
isDefaultRole
}

input user_org_roles_updates {
"""sets the columns of the filtered rows to the given values"""
_set: user_org_roles_set_input

"""filter the rows which have to be updated"""
where: user_org_roles_bool_exp!
}

"""
Expand Down
67 changes: 64 additions & 3 deletions apps/console/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,34 @@ type mutation_root {
updates: [rules_updates!]!
): [rules_mutation_response]

"""
update data of the table: "user_org_roles"
"""
update_user_org_roles(
"""sets the columns of the filtered rows to the given values"""
_set: user_org_roles_set_input

"""filter the rows which have to be updated"""
where: user_org_roles_bool_exp!
): user_org_roles_mutation_response

"""
update single row of the table: "user_org_roles"
"""
update_user_org_roles_by_pk(
"""sets the columns of the filtered rows to the given values"""
_set: user_org_roles_set_input
pk_columns: user_org_roles_pk_columns_input!
): user_org_roles

"""
update multiples rows of table: "user_org_roles"
"""
update_user_org_roles_many(
"""updates to execute, in order"""
updates: [user_org_roles_updates!]!
): [user_org_roles_mutation_response]

"""
update multiples rows of table: "auth.users"
"""
Expand Down Expand Up @@ -3314,6 +3342,7 @@ type user_org_roles {
createdAt: timestamptz!
createdBy: uuid!
id: uuid!
isDefaultRole: Boolean!
organization: String!
role: String!
userId: uuid!
Expand All @@ -3329,6 +3358,7 @@ input user_org_roles_bool_exp {
createdAt: timestamptz_comparison_exp
createdBy: uuid_comparison_exp
id: uuid_comparison_exp
isDefaultRole: Boolean_comparison_exp
organization: String_comparison_exp
role: String_comparison_exp
userId: uuid_comparison_exp
Expand All @@ -3343,6 +3373,11 @@ enum user_org_roles_constraint {
"""
user_org_roles_pkey

"""
unique or primary key constraint on columns "user_id", "organization"
"""
user_org_roles_user_id_organization_default_role_unique

"""
unique or primary key constraint on columns "user_id", "organization", "role"
"""
Expand All @@ -3353,6 +3388,7 @@ enum user_org_roles_constraint {
input type for inserting data into table "user_org_roles"
"""
input user_org_roles_insert_input {
isDefaultRole: Boolean
organization: String
role: String
userId: uuid
Expand Down Expand Up @@ -3383,11 +3419,17 @@ input user_org_roles_order_by {
createdAt: order_by
createdBy: order_by
id: order_by
isDefaultRole: order_by
organization: order_by
role: order_by
userId: order_by
}

"""primary key columns input for table: user_org_roles"""
input user_org_roles_pk_columns_input {
id: uuid!
}

"""
select columns of table "user_org_roles"
"""
Expand All @@ -3401,6 +3443,9 @@ enum user_org_roles_select_column {
"""column name"""
id

"""column name"""
isDefaultRole

"""column name"""
organization

Expand All @@ -3411,6 +3456,13 @@ enum user_org_roles_select_column {
userId
}

"""
input type for updating data in table "user_org_roles"
"""
input user_org_roles_set_input {
isDefaultRole: Boolean
}

"""
Streaming cursor of the table "user_org_roles"
"""
Expand All @@ -3427,17 +3479,26 @@ input user_org_roles_stream_cursor_value_input {
createdAt: timestamptz
createdBy: uuid
id: uuid
isDefaultRole: Boolean
organization: String
role: String
userId: uuid
}

"""
placeholder for update columns of table "user_org_roles" (current role has no relevant permissions)
update columns of table "user_org_roles"
"""
enum user_org_roles_update_column {
"""placeholder (do not use)"""
_PLACEHOLDER
"""column name"""
isDefaultRole
}

input user_org_roles_updates {
"""sets the columns of the filtered rows to the given values"""
_set: user_org_roles_set_input

"""filter the rows which have to be updated"""
where: user_org_roles_bool_exp!
}

"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
if (!isGotoNavigated) return;
handleMessage($flash, toastStore);
// // Clear the flash message to avoid double-toasting.
// $flash = undefined;
});
</script>

Expand Down
34 changes: 24 additions & 10 deletions apps/console/src/lib/graphql/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// import { error } from '@sveltejs/kit';
import { error, redirect } from '@sveltejs/kit';
import { invalidateAll } from '$app/navigation';
import { createClient as createWSClient } from 'graphql-ws';
import { Logger } from '@spectacular/utils';
import { Logger, hasErrorMessage, hasErrorTypes, isErrorType } from '@spectacular/utils';
import { env } from '$env/dynamic/public';
import { browser } from '$app/environment';
import { subscription } from '$houdini/plugins';
Expand Down Expand Up @@ -46,7 +47,7 @@ export default new HoudiniClient({
}
const accessToken = session?.accessToken;
const backendToken = metadata?.backendToken;
const useRole = metadata?.useRole
const useRole = metadata?.useRole;

return {
headers: {
Expand All @@ -56,12 +57,25 @@ export default new HoudiniClient({
}
};
},
// throwOnError: {
// // can be any combination of
// // query, mutation, subscription, and all
// operations: ['all'],
// // the function to call
// error: (errors, ctx) => error(500, `(${ctx.artifact.name}): ` + errors.map((err) => err.message).join('. ') + '.')
// },
throwOnError: {
// can be any combination of
// query, mutation, subscription, and all
operations: ['all'],
// GraphQL error handling on client-side
error: async (errors, ctx) => {
log.error({ ctx, errors });
// if accessToken(AT) expires (15min), reloading the page will refresh AT and set new AT into cookie
// e.g. goto with invalidateAll on current path
if (errors.some(hasErrorMessage('JWTExpired'))) {
await invalidateAll();
} else if (errors.some(hasErrorTypes(['PERMISSION_DENIED', 'UNAUTHENTICATED']))) {
redirect(303, '/auth/signin');
} else if (errors.some(isErrorType('NOT_FOUND'))) {
error(404);
}
// be silent for rest of the errors
// error(500, `(${ctx.artifact.name}): ` + errors.map((err) => err.message).join('. ') + '.')
}
},
plugins: [subClient, ...(browser ? [logMetadata] : [])]
});
30 changes: 30 additions & 0 deletions apps/console/src/lib/nhost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import { env } from '$env/dynamic/public';
// import { env as secrets } from '$env/dynamic/private';

export const NHOST_SESSION_KEY = 'nhostSession';
const isBrowser = typeof window !== 'undefined';

export async function getNhost(cookies: Cookies) {
const nhost = new NhostClient({
subdomain: env.PUBLIC_NHOST_SUBDOMAIN || 'local',
region: env.PUBLIC_NHOST_REGION,
// Disable automations on server-side.
// Need cookie storage for auth trasfering to SSR.
autoSignIn: isBrowser,
autoRefreshToken: isBrowser,
clientStorageType: 'cookie',
// HINT: When set, it is sent as an `x-hasura-admin-secret` header for all GraphQL, Storage, and Serverless Functions requests.
// adminSecret: secrets.HASURA_GRAPHQL_ADMIN_SECRET,
Expand All @@ -29,3 +34,28 @@ export async function getNhost(cookies: Cookies) {

return nhost;
}

/**
* Use this function to set nhost session into cookie - from SignIn/SignUp/auth-miggleware
* @param cookies
* @param session
*/
export function setNhostSessionInCookies(cookies: Cookies, session: NhostSession) {
const expires = new Date();
// * Expire the cookie 60 seconds before the token expires
expires.setSeconds(expires.getSeconds() + session.accessTokenExpiresIn - 60);
console.log({ expires });
// TODO: Set expires based on the actual refresh token expire time
// For now, we're using 30 days so the cookie is not removed when the browser is closed because if `expiers` is omitted, the cookie becomes a session cookie.
cookies.set(NHOST_SESSION_KEY, btoa(JSON.stringify(session)), { path: '/' });
// cookies.set(NHOST_SESSION_KEY, btoa(JSON.stringify(session)), { path: '/', expires });
}

/**
* delete nhost session cookie
* @param cookies
*/
export function removeNhostSessionInCookies(cookies: Cookies) {
cookies.delete(NHOST_SESSION_KEY, { path: '/' });
// cookies.set(NHOST_SESSION_KEY, '', { httpOnly: true, path: '/', maxAge: 0 });
}
Loading

0 comments on commit 5fb331f

Please sign in to comment.