forked from jagregory/cognito-local
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
207 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { UUID } from "../../src/__tests__/patterns"; | ||
import { UserNotFoundError } from "../../src/errors"; | ||
import { attributeValue } from "../../src/services/userPoolClient"; | ||
import { withCognitoSdk } from "./setup"; | ||
|
||
describe( | ||
"CognitoIdentityServiceProvider.deleteUser", | ||
withCognitoSdk((Cognito) => { | ||
it("deletes the current user", async () => { | ||
const client = Cognito(); | ||
|
||
// create the user pool client | ||
const upc = await client | ||
.createUserPoolClient({ | ||
UserPoolId: "test", | ||
ClientName: "test", | ||
}) | ||
.promise(); | ||
|
||
// create a user | ||
const createUserResponse = await client | ||
.adminCreateUser({ | ||
TemporaryPassword: "def", | ||
UserAttributes: [{ Name: "email", Value: "[email protected]" }], | ||
Username: "abc", | ||
UserPoolId: "test", | ||
}) | ||
.promise(); | ||
const userSub = attributeValue( | ||
"sub", | ||
createUserResponse.User?.Attributes | ||
); | ||
|
||
// attempt to login | ||
const initAuthResponse = await client | ||
.initiateAuth({ | ||
ClientId: upc.UserPoolClient?.ClientId!, | ||
AuthFlow: "USER_PASSWORD_AUTH", | ||
AuthParameters: { | ||
USERNAME: "abc", | ||
PASSWORD: "def", | ||
}, | ||
}) | ||
.promise(); | ||
|
||
// change their password on first login | ||
// TODO: replace this with adminSetPassword when it's supported | ||
const respondToAuthChallengeResult = await client | ||
.respondToAuthChallenge({ | ||
ChallengeName: "NEW_PASSWORD_REQUIRED", | ||
Session: initAuthResponse.Session, | ||
ChallengeResponses: { | ||
USERNAME: "abc", | ||
NEW_PASSWORD: "new_password", | ||
}, | ||
ClientId: upc.UserPoolClient?.ClientId!, | ||
}) | ||
.promise(); | ||
|
||
// delete the user with their token | ||
await client | ||
.deleteUser({ | ||
AccessToken: respondToAuthChallengeResult.AuthenticationResult | ||
?.AccessToken!, | ||
}) | ||
.promise(); | ||
|
||
// verify they don't exist anymore | ||
await expect( | ||
client | ||
.adminGetUser({ | ||
Username: "abc", | ||
UserPoolId: "test", | ||
}) | ||
.promise() | ||
).rejects.toEqual(new UserNotFoundError("User does not exist")); | ||
}); | ||
}) | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import jwt from "jsonwebtoken"; | ||
import * as uuid from "uuid"; | ||
import { newMockCognitoClient } from "../__tests__/mockCognitoClient"; | ||
import { MockLogger } from "../__tests__/mockLogger"; | ||
import { MockUserPoolClient } from "../__tests__/mockUserPoolClient"; | ||
import { InvalidParameterError, NotAuthorizedError } from "../errors"; | ||
import PrivateKey from "../keys/cognitoLocal.private.json"; | ||
import { CognitoClient } from "../services"; | ||
import { DeleteUser, DeleteUserTarget } from "./deleteUser"; | ||
import * as TDB from "../__tests__/testDataBuilder"; | ||
|
||
describe("DeleteUser target", () => { | ||
let deleteUser: DeleteUserTarget; | ||
let mockCognitoClient: jest.Mocked<CognitoClient>; | ||
|
||
beforeEach(() => { | ||
mockCognitoClient = newMockCognitoClient(); | ||
|
||
deleteUser = DeleteUser( | ||
{ | ||
cognitoClient: mockCognitoClient, | ||
}, | ||
MockLogger | ||
); | ||
}); | ||
|
||
it("parses token get user by sub", async () => { | ||
const user = TDB.user(); | ||
|
||
MockUserPoolClient.getUserByUsername.mockResolvedValue(user); | ||
|
||
await deleteUser({ | ||
AccessToken: jwt.sign( | ||
{ | ||
sub: "0000-0000", | ||
event_id: "0", | ||
token_use: "access", | ||
scope: "aws.cognito.signin.user.admin", | ||
auth_time: new Date(), | ||
jti: uuid.v4(), | ||
client_id: "test", | ||
username: "0000-0000", | ||
}, | ||
PrivateKey.pem, | ||
{ | ||
algorithm: "RS256", | ||
issuer: `http://localhost:9229/test`, | ||
expiresIn: "24h", | ||
keyid: "CognitoLocal", | ||
} | ||
), | ||
}); | ||
|
||
expect(MockUserPoolClient.deleteUser).toHaveBeenCalledWith(user); | ||
}); | ||
|
||
it("throws if token isn't valid", async () => { | ||
await expect( | ||
deleteUser({ | ||
AccessToken: "blah", | ||
}) | ||
).rejects.toBeInstanceOf(InvalidParameterError); | ||
}); | ||
|
||
it("throws if user doesn't exist", async () => { | ||
MockUserPoolClient.getUserByUsername.mockResolvedValue(null); | ||
|
||
await expect( | ||
deleteUser({ | ||
AccessToken: jwt.sign( | ||
{ | ||
sub: "0000-0000", | ||
event_id: "0", | ||
token_use: "access", | ||
scope: "aws.cognito.signin.user.admin", | ||
auth_time: new Date(), | ||
jti: uuid.v4(), | ||
client_id: "test", | ||
username: "0000-0000", | ||
}, | ||
PrivateKey.pem, | ||
{ | ||
algorithm: "RS256", | ||
issuer: `http://localhost:9229/test`, | ||
expiresIn: "24h", | ||
keyid: "CognitoLocal", | ||
} | ||
), | ||
}) | ||
).rejects.toEqual(new NotAuthorizedError()); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { DeleteUserRequest } from "aws-sdk/clients/cognitoidentityserviceprovider"; | ||
import jwt from "jsonwebtoken"; | ||
import { InvalidParameterError, NotAuthorizedError } from "../errors"; | ||
import { Logger } from "../log"; | ||
import { Services } from "../services"; | ||
import { Token } from "../services/tokens"; | ||
|
||
export type DeleteUserTarget = (req: DeleteUserRequest) => Promise<{}>; | ||
|
||
type DeleteUserServices = Pick<Services, "cognitoClient">; | ||
|
||
export const DeleteUser = ( | ||
{ cognitoClient }: DeleteUserServices, | ||
logger: Logger | ||
): DeleteUserTarget => async (req) => { | ||
const decodedToken = jwt.decode(req.AccessToken) as Token | null; | ||
if (!decodedToken) { | ||
logger.info("Unable to decode token"); | ||
throw new InvalidParameterError(); | ||
} | ||
|
||
const userPool = await cognitoClient.getUserPoolForClientId( | ||
decodedToken.client_id | ||
); | ||
const user = await userPool.getUserByUsername(decodedToken.sub); | ||
if (!user) { | ||
throw new NotAuthorizedError(); | ||
} | ||
|
||
await userPool.deleteUser(user); | ||
|
||
return {}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters