Skip to content

Commit

Permalink
Merge pull request #44 from Authress/add-anti-abuse-hash
Browse files Browse the repository at this point in the history
Add required anti abuse hash to authentication requests.
  • Loading branch information
wparad authored Aug 3, 2024
2 parents dc24019 + b157400 commit 2f418ae
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"builtins",
"embeddable",
"esbuild",
"filemanager"
"filemanager",
"totp"
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This is the changelog for [Authress Login](readme.md).
## 2.5 ##
* Handle `<HTML DOCUMENT></HTML>` improved with better error investigation into the `error.data` property as well.
* Also remove the `AuthUserId` cookie when removing other cookies.
* Add `antiAbuseHash` generation as part of authentication requests

## 2.4 ##
* Prevent silent returns from `authenticate` when a different connectionId is used to have the user log in.
Expand Down
12 changes: 9 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class LoginClient {

// We use endsWith because the issuer will be limited to only the authress custom domain FQDN subdomain, the hostUrl could be a specific subdomain subdomain for the tenant.
// * issuer = tenant.custom.domain, hostUrl = custom.domain => ✓
// * issuer = accountid.login.authress.io, hostUrl = login.authress.io => ✓
// * issuer = accountId.login.authress.io, hostUrl = login.authress.io => ✓

const issuerOrigin = new URL(userData.iss).hostname;
const hostUrlOrigin = new URL(this.hostUrl).hostname;
Expand Down Expand Up @@ -442,11 +442,13 @@ class LoginClient {
}

try {
const antiAbuseHash = await jwtManager.calculateAntiAbuseHash({ connectionId, tenantLookupIdentifier });
const requestOptions = await this.httpClient.patch(`/authentication/${authenticationRequestId}`, true, {
antiAbuseHash,
connectionId, tenantLookupIdentifier, connectionProperties
});

// If authenticate is called from inside the custom login screen then instead return the redirect url and let the caller deal with it. That is, if the federated login provider is the same as the curernt UI, there is no need to do anything special.
// If authenticate is called from inside the custom login screen then instead return the redirect url and let the caller deal with it. That is, if the federated login provider is the same as the current UI, there is no need to do anything special.
if (new URL(requestOptions.data.authenticationUrl).hostname === windowManager.getCurrentLocation().hostname) {
return {
authenticationUrl: requestOptions.data.authenticationUrl
Expand Down Expand Up @@ -548,6 +550,7 @@ class LoginClient {
}

const { codeChallenge } = await jwtManager.getAuthCodes();
const antiAbuseHash = await jwtManager.calculateAntiAbuseHash({ connectionId, tenantLookupIdentifier, applicationId: this.applicationId });

try {
const normalizedRedirectUrl = redirectUrl && new URL(redirectUrl).toString();
Expand All @@ -556,6 +559,7 @@ class LoginClient {
Authorization: `Bearer ${accessToken}`
};
const requestOptions = await this.httpClient.post('/authentication', this.enableCredentials, {
antiAbuseHash,
linkIdentity: true,
redirectUrl: selectedRedirectUrl, codeChallengeMethod: 'S256', codeChallenge,
connectionId, tenantLookupIdentifier,
Expand Down Expand Up @@ -617,6 +621,7 @@ class LoginClient {
}

const { codeVerifier, codeChallenge } = await jwtManager.getAuthCodes();
const antiAbuseHash = await jwtManager.calculateAntiAbuseHash({ connectionId, tenantLookupIdentifier, inviteId, applicationId: this.applicationId });

try {
const normalizedRedirectUrl = redirectUrl && new URL(redirectUrl).toString();
Expand All @@ -626,6 +631,7 @@ class LoginClient {
}

const authResponse = await this.httpClient.post('/authentication', false, {
antiAbuseHash,
redirectUrl: selectedRedirectUrl, codeChallengeMethod: 'S256', codeChallenge,
connectionId, tenantLookupIdentifier, inviteId,
connectionProperties,
Expand All @@ -637,7 +643,7 @@ class LoginClient {
enableCredentials: authResponse.data.enableCredentials, multiAccount
}));

// If authenticate is called from inside the custom login screen then instead return the redirect url and let the caller deal with it. That is, if the federated login provider is the same as the curernt UI, there is no need to do anything special.
// If authenticate is called from inside the custom login screen then instead return the redirect url and let the caller deal with it. That is, if the federated login provider is the same as the current UI, there is no need to do anything special.
if (new URL(authResponse.data.authenticationUrl).hostname === windowManager.getCurrentLocation().hostname) {
return {
authenticationUrl: authResponse.data.authenticationUrl
Expand Down
16 changes: 16 additions & 0 deletions src/jwtManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ class JwtManager {
const codeChallenge = base64url.encode(hashBuffer);
return { codeVerifier, codeChallenge };
}

async calculateAntiAbuseHash(props) {
const timestamp = Date.now();
const valueString = Object.values(props).filter(v => v).join('|');

let fineTuner = 0;
let hash = null;
while (++fineTuner) {
hash = base64url.encode(await (window.crypto || window.msCrypto).subtle.digest('SHA-256', new TextEncoder().encode(`${timestamp};${fineTuner};${valueString}`)));
if (hash.match(/^00/)) {
break;
}
}

return `v1;${timestamp};${fineTuner};${hash}`;
}
}

module.exports = new JwtManager();

0 comments on commit 2f418ae

Please sign in to comment.