mirror of
https://github.com/PretendoNetwork/account.git
synced 2026-10-02 23:27:56 -05:00
chore: centralize NNID username/password rule checks
removes a ton of duplicated code between gRPC, API, and NNAS
This commit is contained in:
@@ -6,7 +6,22 @@ import moment from 'moment';
|
||||
import hcaptcha from 'hcaptcha';
|
||||
import Mii from 'mii-js';
|
||||
import { doesPNIDExist, connection as databaseConnection } from '@/database';
|
||||
import { isValidBirthday, getAgeFromDate, nintendoPasswordHash, sendConfirmationEmail } from '@/util';
|
||||
import {
|
||||
isValidBirthday,
|
||||
getAgeFromDate,
|
||||
checkNNIDUsernameMinLength,
|
||||
checkNNIDUsernameMaxLength,
|
||||
checkNNIDUsernameValidCharacters,
|
||||
checkNNIDUsernamePunctuationStart,
|
||||
checkNNIDUsernamePunctuationEnd,
|
||||
checkNNIDUsernameDuplicate,
|
||||
checkNNIDPasswordMinLength,
|
||||
checkNNIDPasswordMaxLength,
|
||||
checkNNIDPasswordAtLeast2CharacterGroups,
|
||||
checkNNIDPasswordRepeatCharacters,
|
||||
nintendoPasswordHash,
|
||||
sendConfirmationEmail
|
||||
} from '@/util';
|
||||
import IP2LocationManager from '@/ip2location';
|
||||
import { SystemType } from '@/types/common/system-types';
|
||||
import { TokenType } from '@/types/common/token-types';
|
||||
@@ -21,17 +36,6 @@ import type { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const PNID_VALID_CHARACTERS_REGEX = /^[\w\-.]*$/;
|
||||
const PNID_PUNCTUATION_START_REGEX = /^[_\-.]/;
|
||||
const PNID_PUNCTUATION_END_REGEX = /[_\-.]$/;
|
||||
const PNID_PUNCTUATION_DUPLICATE_REGEX = /[_\-.]{2,}/;
|
||||
|
||||
// * This sucks
|
||||
const PASSWORD_WORD_OR_NUMBER_REGEX = /(?=.*[a-zA-Z])(?=.*\d).*/;
|
||||
const PASSWORD_WORD_OR_PUNCTUATION_REGEX = /(?=.*[a-zA-Z])(?=.*[_\-.]).*/;
|
||||
const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX = /(?=.*\d)(?=.*[_\-.]).*/;
|
||||
const PASSWORD_REPEATED_CHARACTER_REGEX = /(.)\1\1/;
|
||||
|
||||
const DEFAULT_MII_DATA = Buffer.from('AwAAQOlVognnx0GC2/uogAOzuI0n2QAAAEBEAGUAZgBhAHUAbAB0AAAAAAAAAEBAAAAhAQJoRBgmNEYUgRIXaA0AACkAUkhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGm9', 'base64');
|
||||
|
||||
/**
|
||||
@@ -166,7 +170,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.length < 6) {
|
||||
if (!checkNNIDUsernameMinLength(username)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -176,7 +180,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.length > 16) {
|
||||
if (!checkNNIDUsernameMaxLength(username)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -186,8 +190,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PNID_VALID_CHARACTERS_REGEX.test(username)) {
|
||||
console.log(Buffer.from(username));
|
||||
if (!checkNNIDUsernameValidCharacters(username)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -197,7 +200,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_START_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernamePunctuationStart(username)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -207,7 +210,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_END_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernamePunctuationEnd(username)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -217,7 +220,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_DUPLICATE_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernameDuplicate(username)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -259,7 +262,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
if (!checkNNIDPasswordMinLength(password)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -269,7 +272,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length > 16) {
|
||||
if (!checkNNIDPasswordMaxLength(password)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -289,7 +292,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PASSWORD_WORD_OR_NUMBER_REGEX.test(password) && !PASSWORD_WORD_OR_PUNCTUATION_REGEX.test(password) && !PASSWORD_NUMBER_OR_PUNCTUATION_REGEX.test(password)) {
|
||||
if (!checkNNIDPasswordAtLeast2CharacterGroups(password)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
@@ -299,7 +302,7 @@ router.post('/', webRegisterRatelimit, async (request: express.Request, response
|
||||
return;
|
||||
}
|
||||
|
||||
if (PASSWORD_REPEATED_CHARACTER_REGEX.test(password)) {
|
||||
if (!checkNNIDPasswordRepeatCharacters(password)) {
|
||||
response.status(400).json({
|
||||
app: 'api',
|
||||
status: 400,
|
||||
|
||||
@@ -6,7 +6,20 @@ import moment from 'moment';
|
||||
import hcaptcha from 'hcaptcha';
|
||||
import Mii from 'mii-js';
|
||||
import { doesPNIDExist, connection as databaseConnection } from '@/database';
|
||||
import { nintendoPasswordHash, sendConfirmationEmail } from '@/util';
|
||||
import {
|
||||
checkNNIDUsernameMinLength,
|
||||
checkNNIDUsernameMaxLength,
|
||||
checkNNIDUsernameValidCharacters,
|
||||
checkNNIDUsernamePunctuationStart,
|
||||
checkNNIDUsernamePunctuationEnd,
|
||||
checkNNIDUsernameDuplicate,
|
||||
checkNNIDPasswordMinLength,
|
||||
checkNNIDPasswordMaxLength,
|
||||
checkNNIDPasswordAtLeast2CharacterGroups,
|
||||
checkNNIDPasswordRepeatCharacters,
|
||||
nintendoPasswordHash,
|
||||
sendConfirmationEmail
|
||||
} from '@/util';
|
||||
import { LOG_ERROR } from '@/logger';
|
||||
import { PNID } from '@/models/pnid';
|
||||
import { NEXAccount } from '@/models/nex-account';
|
||||
@@ -19,17 +32,6 @@ import type { RegisterRequest, DeepPartial } from '@pretendonetwork/grpc/api/reg
|
||||
import type { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account';
|
||||
import type { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
|
||||
const PNID_VALID_CHARACTERS_REGEX = /^[\w\-.]*$/;
|
||||
const PNID_PUNCTUATION_START_REGEX = /^[_\-.]/;
|
||||
const PNID_PUNCTUATION_END_REGEX = /[_\-.]$/;
|
||||
const PNID_PUNCTUATION_DUPLICATE_REGEX = /[_\-.]{2,}/;
|
||||
|
||||
// * This sucks
|
||||
const PASSWORD_WORD_OR_NUMBER_REGEX = /(?=.*[a-zA-Z])(?=.*\d).*/;
|
||||
const PASSWORD_WORD_OR_PUNCTUATION_REGEX = /(?=.*[a-zA-Z])(?=.*[_\-.]).*/;
|
||||
const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX = /(?=.*\d)(?=.*[_\-.]).*/;
|
||||
const PASSWORD_REPEATED_CHARACTER_REGEX = /(.)\1\1/;
|
||||
|
||||
const DEFAULT_MII_DATA = Buffer.from('AwAAQOlVognnx0GC2/uogAOzuI0n2QAAAEBEAGUAZgBhAHUAbAB0AAAAAAAAAEBAAAAhAQJoRBgmNEYUgRIXaA0AACkAUkhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGm9', 'base64');
|
||||
|
||||
export async function register(request: RegisterRequest): Promise<DeepPartial<LoginResponse>> {
|
||||
@@ -65,27 +67,27 @@ export async function register(request: RegisterRequest): Promise<DeepPartial<Lo
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Must enter a username');
|
||||
}
|
||||
|
||||
if (username.length < 6) {
|
||||
if (!checkNNIDUsernameMinLength(username)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Username is too short');
|
||||
}
|
||||
|
||||
if (username.length > 16) {
|
||||
if (!checkNNIDUsernameMaxLength(username)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Username is too long');
|
||||
}
|
||||
|
||||
if (!PNID_VALID_CHARACTERS_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernameValidCharacters(username)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Username contains invalid characters');
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_START_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernamePunctuationStart(username)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Username cannot begin with punctuation characters');
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_END_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernamePunctuationEnd(username)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Username cannot end with punctuation characters');
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_DUPLICATE_REGEX.test(username)) {
|
||||
if (!checkNNIDUsernameDuplicate(username)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Two or more punctuation characters cannot be used in a row');
|
||||
}
|
||||
|
||||
@@ -109,7 +111,7 @@ export async function register(request: RegisterRequest): Promise<DeepPartial<Lo
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Must enter a password');
|
||||
}
|
||||
|
||||
if (password.length < 6 || password.length > 16) {
|
||||
if (!checkNNIDPasswordMinLength(password) || !checkNNIDPasswordMaxLength(password)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Password must be between 6 and 16 characters long');
|
||||
}
|
||||
|
||||
@@ -117,11 +119,11 @@ export async function register(request: RegisterRequest): Promise<DeepPartial<Lo
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Password cannot be the same as username');
|
||||
}
|
||||
|
||||
if (!PASSWORD_WORD_OR_NUMBER_REGEX.test(password) && !PASSWORD_WORD_OR_PUNCTUATION_REGEX.test(password) && !PASSWORD_NUMBER_OR_PUNCTUATION_REGEX.test(password)) {
|
||||
if (!checkNNIDPasswordAtLeast2CharacterGroups(password)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Password must have combination of letters, numbers, and/or punctuation characters');
|
||||
}
|
||||
|
||||
if (PASSWORD_REPEATED_CHARACTER_REGEX.test(password)) {
|
||||
if (!checkNNIDPasswordRepeatCharacters(password)) {
|
||||
throw new ServerError(Status.INVALID_ARGUMENT, 'Password may not have 3 repeating characters');
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import Mii from 'mii-js';
|
||||
import deviceCertificateMiddleware from '@/middleware/device-certificate';
|
||||
import { deviceRatelimit } from '@/middleware/ratelimit';
|
||||
import { connection as databaseConnection, doesPNIDExist, getPNIDProfileJSONByPID } from '@/database';
|
||||
import { isValidBirthday, getAgeFromDate, isObject, getValueFromHeaders, nintendoPasswordHash, sendConfirmationEmail, sendPNIDDeletedEmail } from '@/util';
|
||||
import { isValidBirthday, getAgeFromDate, checkNNIDUsernameValid, checkNNIDPasswordValid, isObject, getValueFromHeaders, nintendoPasswordHash, sendConfirmationEmail, sendPNIDDeletedEmail } from '@/util';
|
||||
import IP2LocationManager from '@/ip2location';
|
||||
import { PNID } from '@/models/pnid';
|
||||
import { NEXAccount } from '@/models/nex-account';
|
||||
@@ -18,17 +18,6 @@ import type { HydratedPNIDDocument } from '@/types/mongoose/pnid';
|
||||
import type { HydratedNEXAccountDocument } from '@/types/mongoose/nex-account';
|
||||
import type { Person } from '@/types/services/nnas/person';
|
||||
|
||||
const PNID_VALID_CHARACTERS_REGEX = /^[\w\-.]*$/;
|
||||
const PNID_PUNCTUATION_START_REGEX = /^[_\-.]/;
|
||||
const PNID_PUNCTUATION_END_REGEX = /[_\-.]$/;
|
||||
const PNID_PUNCTUATION_DUPLICATE_REGEX = /[_\-.]{2,}/;
|
||||
|
||||
// * This sucks
|
||||
const PASSWORD_WORD_OR_NUMBER_REGEX = /(?=.*[a-zA-Z])(?=.*\d).*/;
|
||||
const PASSWORD_WORD_OR_PUNCTUATION_REGEX = /(?=.*[a-zA-Z])(?=.*[_\-.]).*/;
|
||||
const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX = /(?=.*\d)(?=.*[_\-.]).*/;
|
||||
const PASSWORD_REPEATED_CHARACTER_REGEX = /(.)\1\1/;
|
||||
|
||||
// * Taken from https://github.com/cemu-project/Cemu/blob/5ead58008dd984f614e2cb38bd9cb69bd77fd1bb/src/Cemu/ncrypto/ncrypto.cpp#L825
|
||||
// * which Cemu uses to build it's AuthInfo struct, which is what is used to populate the X-Nintendo-Country header.
|
||||
// * This header uses the same values as the "country" field in the NNID account data. Assumed to be correct
|
||||
@@ -311,64 +300,7 @@ router.post('/', deviceRatelimit, deviceCertificateMiddleware, async (request: e
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO - Centralize this somewhere. Currently these same checks are being done in the gRPC and API service as well
|
||||
if (!person.user_id) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'userId',
|
||||
code: '1104',
|
||||
message: 'User ID format is not valid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PNID_VALID_CHARACTERS_REGEX.test(person.user_id)) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'userId',
|
||||
code: '1104',
|
||||
message: 'User ID format is not valid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_START_REGEX.test(person.user_id)) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'userId',
|
||||
code: '1104',
|
||||
message: 'User ID format is not valid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_END_REGEX.test(person.user_id)) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'userId',
|
||||
code: '1104',
|
||||
message: 'User ID format is not valid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (PNID_PUNCTUATION_DUPLICATE_REGEX.test(person.user_id)) {
|
||||
if (!person.user_id || !checkNNIDUsernameValid(person.user_id)) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
@@ -413,50 +345,7 @@ router.post('/', deviceRatelimit, deviceCertificateMiddleware, async (request: e
|
||||
// * return;
|
||||
// * }
|
||||
|
||||
// TODO - Centralize this somewhere, all the same error
|
||||
if (!person.password) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'password',
|
||||
code: '0002',
|
||||
message: 'password format is invalid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (person.password.length < 6 || person.password.length > 16) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'password',
|
||||
code: '0002',
|
||||
message: 'password format is invalid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PASSWORD_WORD_OR_NUMBER_REGEX.test(person.password) && !PASSWORD_WORD_OR_PUNCTUATION_REGEX.test(person.password) && !PASSWORD_NUMBER_OR_PUNCTUATION_REGEX.test(person.password)) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
cause: 'password',
|
||||
code: '0002',
|
||||
message: 'password format is invalid'
|
||||
}
|
||||
}
|
||||
}).end());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (PASSWORD_REPEATED_CHARACTER_REGEX.test(person.password)) {
|
||||
if (!person.password || !checkNNIDPasswordValid(person.password)) {
|
||||
response.status(400).send(xmlbuilder.create({
|
||||
errors: {
|
||||
error: {
|
||||
|
||||
82
src/util.ts
82
src/util.ts
@@ -35,6 +35,88 @@ if (!disabledFeatures.s3) {
|
||||
});
|
||||
}
|
||||
|
||||
const NNID_VALID_CHARACTERS_REGEX = /^[\w\-.]*$/;
|
||||
const NNID_PUNCTUATION_START_REGEX = /^[_\-.]/;
|
||||
const NNID_PUNCTUATION_END_REGEX = /[_\-.]$/;
|
||||
const NNID_PUNCTUATION_DUPLICATE_REGEX = /[_\-.]{2,}/;
|
||||
|
||||
// * This sucks
|
||||
const PASSWORD_WORD_OR_NUMBER_REGEX = /(?=.*[a-zA-Z])(?=.*\d).*/;
|
||||
const PASSWORD_WORD_OR_PUNCTUATION_REGEX = /(?=.*[a-zA-Z])(?=.*[_\-.]).*/;
|
||||
const PASSWORD_NUMBER_OR_PUNCTUATION_REGEX = /(?=.*\d)(?=.*[_\-.]).*/;
|
||||
const PASSWORD_REPEATED_CHARACTER_REGEX = /(.)\1\1/;
|
||||
|
||||
// * Checks if the input NNID username passes all validation rules
|
||||
export function checkNNIDUsernameValid(username: string): boolean {
|
||||
return checkNNIDUsernameLength(username) && checkNNIDUsernameValidCharacters(username) && checkNNIDUsernamePunctuationStart(username) && checkNNIDUsernamePunctuationEnd(username) && checkNNIDUsernameDuplicate(username);
|
||||
}
|
||||
|
||||
// * Ensures the NNID username meets the expected size requirements
|
||||
export function checkNNIDUsernameLength(username: string): boolean {
|
||||
return checkNNIDUsernameMinLength(username) && checkNNIDUsernameMaxLength(username);
|
||||
}
|
||||
|
||||
// * Ensures the NNID username is at least 6 characters long
|
||||
export function checkNNIDUsernameMinLength(username: string): boolean {
|
||||
return username.length >= 6;
|
||||
}
|
||||
|
||||
// * Ensures the NNID username is at most 16 characters long
|
||||
export function checkNNIDUsernameMaxLength(username: string): boolean {
|
||||
return username.length <= 16;
|
||||
}
|
||||
|
||||
// * Ensures the NNID username only contains valid characters
|
||||
export function checkNNIDUsernameValidCharacters(username: string): boolean {
|
||||
return NNID_VALID_CHARACTERS_REGEX.test(username);
|
||||
}
|
||||
|
||||
// * Ensures the NNID username does not start with a punctuation character
|
||||
export function checkNNIDUsernamePunctuationStart(username: string): boolean {
|
||||
return !NNID_PUNCTUATION_START_REGEX.test(username);
|
||||
}
|
||||
|
||||
// * Ensures the NNID username does not end with a punctuation character
|
||||
export function checkNNIDUsernamePunctuationEnd(username: string): boolean {
|
||||
return !NNID_PUNCTUATION_END_REGEX.test(username);
|
||||
}
|
||||
|
||||
// * Ensures the NNID username does not use a punctuation character more than twice in a row
|
||||
export function checkNNIDUsernameDuplicate(username: string): boolean {
|
||||
return !NNID_PUNCTUATION_DUPLICATE_REGEX.test(username);
|
||||
}
|
||||
|
||||
// * Checks if the input NNID password passes all validation rules
|
||||
export function checkNNIDPasswordValid(password: string): boolean {
|
||||
return checkNNIDPasswordLength(password) && checkNNIDPasswordAtLeast2CharacterGroups(password) && checkNNIDPasswordRepeatCharacters(password);
|
||||
}
|
||||
|
||||
// * Ensures the NNID password meets the expected size requirements
|
||||
export function checkNNIDPasswordLength(password: string): boolean {
|
||||
return checkNNIDPasswordMinLength(password) && checkNNIDPasswordMaxLength(password);
|
||||
}
|
||||
|
||||
// * Ensures the NNID password is at least 6 characters long
|
||||
export function checkNNIDPasswordMinLength(password: string): boolean {
|
||||
return password.length >= 6;
|
||||
}
|
||||
|
||||
// * Ensures the NNID password is at most 16 characters long
|
||||
export function checkNNIDPasswordMaxLength(password: string): boolean {
|
||||
return password.length <= 16;
|
||||
}
|
||||
|
||||
// * Ensures the NNID password has a mix of letters, numbers, and/or punctuation characters.
|
||||
// * Passwords must contain letters from at least 2 of the 3 groups (letters, numbers, and punctuation)
|
||||
export function checkNNIDPasswordAtLeast2CharacterGroups(password: string): boolean {
|
||||
return PASSWORD_WORD_OR_NUMBER_REGEX.test(password) || PASSWORD_WORD_OR_PUNCTUATION_REGEX.test(password) || PASSWORD_NUMBER_OR_PUNCTUATION_REGEX.test(password);
|
||||
}
|
||||
|
||||
// * Ensures the NNID password does not have 3 or more repeating characters
|
||||
export function checkNNIDPasswordRepeatCharacters(password: string): boolean {
|
||||
return PASSWORD_REPEATED_CHARACTER_REGEX.test(password);
|
||||
}
|
||||
|
||||
export function nintendoPasswordHash(password: string, pid: number): string {
|
||||
const pidBuffer = Buffer.alloc(4);
|
||||
pidBuffer.writeUInt32LE(pid);
|
||||
|
||||
Reference in New Issue
Block a user