Examples: Common Use Cases
Common usage of events and hooks to modify the behavior of an authentication on Authgear
Here are some example of using Hook to add actions to Authgear during the authentication flow. This allows extension of security features such as more granular access control, adaptive MFA and variable rate limit.
Navigate to Advanced > Hooks in the Authgear portal. Next, click on Add under the Blocking Events section. Select "TypeScript" and then "Edit Script" to write code that will be executed on your desired events.
Webhook can also be used if you want to run the code in your server. See Webhooks for more details.
Allow signups only from inside the corporate network
You can use the user.pre_create event to allow signups only from a certain IP.
For example, only allow signup when user is in the network 123.45.67.89
:
export default async function (
e: EventUserPreCreate
): Promise<HookResponse> {
const allowedIp = "123.45.67.89"; // The IP address of the corporate network
if (e.context.ip_address == allowedIp) {
return {
is_allowed: true,
};
} else {
return {
is_allowed: false,
reason: "You are not allow to sign up to this organisation",
title: "Sign-up not allowed!",
};
}
}
Allowing access for specific geo location
You can use the authentication.post_identified event to block or allow users from a certain location.
For example, only allow signups and logins from Hong Kong:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
if (e.context.geo_location_code === "HK") {
return {
is_allowed: true,
};
} else {
return {
is_allowed: false,
};
}
}
Allow access according to user roles for a certain application
You can use authentication.post_identified to block or allow user authenticate in a certain app according to their roles, standard or custom attributes.
For example, if you want to allow only users with role sales
to access the app crm-system
with client id is c8da9b322e1f494e
:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
if (
e.context.client_id === "c8da9b322e1f494e" &&
e.payload.authentication_context?.authentication_flow?.type != null &&
["login", "signup"].includes(e.payload.authentication_context?.authentication_flow?.type)
) {
const user = e.payload.authentication_context.user
if (user?.roles?.includes("sales")) {
return {
is_allowed: true,
};
} else if (user?.custom_attributes?.can_access_crm === "true") {
// Alternatively, use custom_attributes to determine if the user is allowed to access the app
return {
is_allowed: true,
};
} else {
return {
is_allowed: false,
reason: "You don't have permission to use this app"
};
}
}
// Allow login or signups of other clients
return {
is_allowed: true,
};
}
Allow access according to email domain
You can use authentication.post_identified to block user from signing up in your system if they are not using a specific email domain.
For example, you only want user with email domain @authgear.com
to be able to signup:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
const email = e.payload.identification.identity?.claims?.email
if (typeof email === "string" && email.endsWith("@authgear.com")) {
return {
is_allowed: true,
};
}
// Block signup of all other emails
return {
is_allowed: false,
};
}
Block login during weekends
You can use authentication.post_identified to block user from logging in during weekends.
For example, if your business only operate during weekdays, therefore you do not want any user login during weekends:
export default async function (
e: EventAuthenticationPostIdentified
): Promise<EventAuthenticationPostIdentifiedHookResponse> {
const today = new Date();
// 0 is sunday, and 6 is saturday
if (today.getDay() === 0 || today.getDay() === 6) {
return {
is_allowed: false,
};
}
return {
is_allowed: true,
};
}
Note: Even login is blocked during weekends, refresh token and access tokens issued during weekdays will not be invalidated.
Block user login according to authentication method
You can use authentication.pre_authenticated to block user login according to the AMR (Authentication Method Reference) used during authentication.
For example, if you want to block users who DID NOT use mfa
(multi-factor authentication) during the authentication:
export default async function (
e: EventAuthenticationPreAuthenticated
): Promise<EventAuthenticationPreAuthenticatedHookResponse> {
if (!e.payload.authentication_context.amr?.includes("mfa")) {
return {
is_allowed: false,
};
}
return {
is_allowed: true,
};
}
Enable bot protection under specific conditions
Use authentication.pre_initialize to enable bot protection under specific conditions.
For example, if you want to display captcha only for user outside Hong Kong:
Firstly, enable Bot Protection in the portal and set the requirements to "Never"
Then, return bot_protection.mode
in your authentication.pre_initialize
hook:
export default async function (
e: EventAuthenticationPreInitialize
): Promise<EventAuthenticationPreInitializeHookResponse> {
if (e.context.geo_location_code !== "HK") {
return {
is_allowed: true,
bot_protection: {
mode: "always",
},
};
}
// Else, simply allow the login
return {
is_allowed: true,
};
}
This overrides the original mode
of bot_protection in your config. Therefore, bot_protection will be turned on if the user is trying to signup or login outside Hong Kong.
Adaptive MFA: Require MFA only for users with high risk
You can use authentication.pre_authenticated to implement Adaptive MFA.
For example, you consider logins from outside HK
is at a higher risk, therefore MFA should be required:
export default async function (
e: EventAuthenticationPreAuthenticated
): Promise<EventAuthenticationPreAuthenticatedHookResponse> {
if (e.context.geo_location_code !== "HK") {
return {
// Allow the login with a mfa contraint
is_allowed: true,
constraints: {
amr: ["mfa"],
},
};
}
// Else, simply allow the login
return {
is_allowed: true,
};
}
If constraints.amr
with value ["mfa"]
is returned in the response, depending on the authentication flow type:
Signup / Promote: If the user does not have any secondary authenticator setup during the flow, a step will be added at the end of the flow to force user to setup a secondary authenticator.
Login / Re-authentication: If the user does not use any secondary authenticator during the flow, a step will be added at the end of the flow for 2FA. If the user never setup 2FA before, the flow fail.
Account Recovery: No effect, because account recovery does not support 2FA.
Learn more about AMR in AMR (Authentication Method Reference).
Advanced: Applying stricter rate limits for authentications for a certain IP range
You can apply a stricter rate limit in an authentication for a certain IP range flow using hooks.
For example, you want to limit account enumeration to 5 per minute if the request origins from a data center IP address, and 10 attempts per minute in any other requests.
Adjust base rate limit
Firstly, add this rate limit configuration in the project config. (Portal > Advanced > Edit Config)
authentication:
rate_limits:
account_enumeration:
per_ip:
enabled: true
period: 1m
burst: 10
This sets the base rate limit of account enumeration to 10/minute.
Override rate limit in the hook by a weight
Then, create the following hook in the authentication.pre_initialize event:
function ipToBinary(ip: string): string {
return ip
.split(".")
.map((octet) => parseInt(octet, 10).toString(2).padStart(8, "0"))
.join("");
}
function cidrToPrefix(cidr: string): string {
const [ip, lengthStr] = cidr.split("/");
const prefixLength = parseInt(lengthStr, 10);
const ipBinary = ipToBinary(ip);
return ipBinary.substring(0, prefixLength);
}
export default async function (
e: EventAuthenticationPreInitialize
): Promise<EventAuthenticationPreInitializeHookResponse> {
if (e.context.ip_address == null) {
// ip unknown, block it.
return {
is_allowed: false,
};
}
const ipBinary = ipToBinary(e.context.ip_address);
// ip ranges from https://www.gstatic.com/ipranges/cloud.json
const dataCenterIPRanges = ["34.125.0.0/16", "34.124.24.0/21"];
// 34.125.0.0/16
if (dataCenterIPRanges.some((cidr) => ipBinary.startsWith(cidrToPrefix(cidr)))) {
return {
is_allowed: true,
rate_limits: {
"authentication.account_enumeration": {
weight: 2,
},
},
};
} else {
return {
is_allowed: true,
};
}
}
By setting "rate_limits.authentication.account_enumeration.weight"
to 2, any attempt of account enumeration will contribute 2
attempts to the rate limit. Therefore, only 5 attempts are allowed in 1 minute. (10 / 2 = 5). Learn more in Override Rate Limits
Last updated
Was this helpful?