I have implemented a login system in C, where the user has three chances to enter the correct user_id and password, if you finish all, the program close.
The problem now is that if the first attempt failed, and the second attempt key incorrect
credentials, the system wont authenticate the user, it only works on first attempt if the credentials is correct.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char user_id[50];
char name[50];
char password[50];
char email[50];
char role[50];
} users;
int main() {
login_menu;
return 0;
}
void login_menu() {
printf("Login page");
FILE *users_file = fopen("users.txt", "r");
int authenticated = 0;
int login_attempts = 0;
const int max_login_attempts = 3;
while (login_attempts < max_login_attempts) {
users user;
int valid_user_id = 0;
char user_id[50];
char password[50];
printf("Please enter your User ID (TP number or Tutor code) down below: \n");
scanf("%s", user_id);
printf("Please enter your password down below: \n");
scanf("%s", password);
if ((user_id[0] == 'T' && user_id[1] == 'P') || (user_id[0] == 't' && user_id[1] == 'p')) {
valid_user_id = 1;
for (int i = 0; i < 50; i++) {
user_id[i] = user_id[i + 2];
}
} else if ((user_id[0] == 'T') || (user_id[0] == 't')) {
valid_user_id = 1;
for (int i = 0; i < 50; i++) {
user_id[i] = user_id[i + 1];
}
} else if ((user_id[0] == 'A') || (user_id[0] == 'a')) {
valid_user_id = 1;
for (int i = 0; i < 50; i++) {
user_id[i] = user_id[i + 1];
}
}
if (valid_user_id) {
// use while loop to read the file line by line
while (fscanf(users_file, "%s %s %s %s %s \n", user.user_id, user.name, user.password,
user.email, user.role) != EOF) {
if (strcmp(user.user_id, user_id) == 0 && strcmp(user.password, password) == 0) {
authenticated = 1;
break;
}
}
}
if (authenticated) {
fclose(users_file);
printf("Login successful %s (%s) !\n", user.name, user.role);
dashboard_menu(user);
break;
} else {
login_attempts++;
printf("Login id or password is incorrect, %d attempts left. Please try again.\n", (3 - login_attempts));
printf("Invalid c");
}
}
if (authenticated == 0) {
printf("Max login attempts reached. Exiting program.\n");
}
fclose(users_file);
}
here is the txt file users.txt, for the data of the users
988955 John 123456 admin@apu.edu.my admin
265663 Mary 123456 marry@apu.edu.my tutor
009650 Peter 123456 peter@apu.edu.my tutor
544654 James 123456 james@apu.edu.my tutor
577001 Johnny 123456 john@apu.edu.my tutor
683357 David 123456 david@apu.edu.my tutor
>Solution :
The problem is that you read through all of your users_file via fscanf() on the first attempt, so then if the user needs to make a second attempt, the seek-position of users_file file-handle is already at the end of the file, so fscanf() won’t return any more of the file’s contents.
The easy fix is just to call rewind(users_file) before doing your loop-of-fscanf() calls — the rewind() call will move the read-position of the file-handle back to the beginning of the file, so you can read through all the entries in the file again, the same way you did the first time. (alternatively, you could call fclose() on your file-handle than then re-open the file with another call to fopen(), that would have a similar effect)
Btw note that your calls to scanf() are extremely unsafe — if the user is malicious (or just careless), he can enter more than 50 characters as his input, and cause scanf() to write past the end of your user_id and/or password arrays, corrupting your program’s stack and potentially allowing the user to seize control of the program’s execution. It’s highly recommended that you replace all calls to scanf("%s", blah); with something that takes the length of the output-buffer as an argument instead (e.g. fgets(blah, sizeof(blah), stdin);), so that any potential buffer-overwrites can be avoided.

