This project create aspnet core Identity http endpoints with Minimal API features and a Reactjs app that implements the ASPNET Identity UI screens. It uses IUserManager
, ISigningManager
and other existing ASPNET Core Identity services. The project implements a two factor auth using email, sms (Twilio) and TOTP Authenticator app. Upon signing up and email confirmation, the user can choose to set-up one of the provided two-factor (Email, Phone, Authenticator App) methods. Below is the list of provided http endpoints:
When the user successfully authenticate (After two-factor), the auth endpoint generates and returns two tokens:
- Access token: This is a custom JWT token with user data as claims that can be used for consuming other protected endpoints
- Refresh token: This is an opaque UUID token that gets generated and stored in the DB. the value of this token is the same as the JTI value for access token. It can be used to refresh the access token, though the refresh endpoint.
This project uses three strategies for JWT token (Access token) invalidation. These three strategies rely on the DB queries, therefore making the jwt scenarios stateful. You can configure your preferred strategy using below code:
builder.Services.AddJwtRevocationStrategy(options =>
{
options.StrategyName = JwtRevocationStrategyConstants.JtiMatchter;
//options.StrategyName = JwtRevocationStrategyConstants.AllowList;
//options.StrategyName = JwtRevocationStrategyConstants.Denylist;
});
In case you don't want to bother about token invalidation, you can just configure your JWT tokens to be short lived (10-15 min) and use the refresh token endpoint to re-issue the access token and keep the user logged. You'll need, however have to change the authentication configuration to use the jwtbearer validation mechanisms that come with ASPNET Core.
We store the access token's jti (unique identifier) with the user info. This is an additional column in the user info table. Upon a successful authentication and token generation, this jti value is saved to the user table. When the user calls the /auth/logout
endpoint, we set the jti column to null
. To validate if a token has not been revoked, the custom auth middleware queries the DB database for the jti value and compares it with the provided token jti value. if the values match, the calls goes through, otherwise the call is rejected with 401 http status code.
With this strategt, we create an allow list table that stores the userId, information from the token (jti) and token experition timestamp. Rather than storing the full blown JWT, we store the jti instead. When the user successfully authenticates, we generate a jwt token and stores its jti in the allowed list table. To validate if the token has not been revoked, the custom middleware decodes the token, gets its jti matcher and queries the allowed list table and compares the token jti values and the experation timestamp. The user can setup a batch process for cleaning up this table based on token experition timestamp.
When the token is revoked either through calling /auth/logout
or auth/revoke
endpoints, the token's jti, user Id and token experition timestamp are stored in a disallowed table. When the user calls a protected endpoint, the custom middleware decodes the token, queries the database for the jti value and compares the decoded jti claim to the value from the DB. If they match, then we know the token has been revoked and therefore the middleware rejects the request with 401 http status code.
- The backend relies on ef core. You'll need to generate the ef core schemas
dotnet ef migrations add InitialIdenityMigration
and thendotnet ef database update
to create your DB and tables. - For email confirmation, phone number confirmation and two factor auth to work, you'll need to provide the following secrets:
{
"Twilio:TwilioPhoneNumber": "Your twilio number",
"Twilio:AuthToken": "Your twilio auth token",
"Twilio:AccountSID": "Your twilio account sid",
"SendGridEmail:ApiKey": "your sendgrip API key",
"Twilio:VerificationServiceSID": "Your twilio verification sid for phone number verifications",
"JwtSettings:Issuer": "https://localhost:7115 - Can be replaced with your own",
"JwtSettings:Audience": "https://localhost:7115 - Can be replaced with your own",
"JwtSettings:TokenSecretKey": "You jwt token secrets used for signing the tokens"
}
- Ensure you have the latest nodejs and run the following command:
npm install
npm start
Your app should be served at port 3000. Depending on the port for your backend, you may need to change the following value of the url in the .env.development
:
REACT_APP_API_URL=https://localhost:7115
The user can also log in by redeeming one of the authenticator recovery codes.
3. Other available screens (Forgot Passowrd flow, Login With Authenticator Code, Validate Email, Confirm Phone Number)
- Implement the cookie auth for Single page apps hosted on the same domain as the backend and server side rendered SPAs.
- Social media signing
- Encrypted JWT
- Deploy these apps to Azure with terraform.
- Write tests for the Minimal API backend and reactjs frontend.