Changeset a26f6a1


Ignore:
Timestamp:
08/09/22 16:09:32 (2 years ago)
Author:
Danilo <danilo.najkov@…>
Branches:
master
Children:
a569b7c
Parents:
899b19d
Message:

full auth flow

Files:
9 added
15 edited
1 moved

Legend:

Unmodified
Added
Removed
  • frontend/src/App.js

    r899b19d ra26f6a1  
    66import {Spin} from "antd";
    77import React, {useEffect, useState} from "react";
    8 import {Login, Register} from "./auth";
     8import {Login, Register} from "./Auth/auth";
    99import Dashboard from "./Dashboard";
    1010import Reservations from "./Dashboard/Reservations";
     
    1313import Menu from "./Dashboard/Menu";
    1414import Review from "./Dashboard/Review";
     15import Confirm from "./Auth/Confirm";
     16import SendConfirm from "./Auth/SendConfirm";
     17import SendReset from "./Auth/SendReset";
     18import Reset from "./Auth/Reset";
    1519
    1620
    1721function App() {
    1822  const [user, setUser] = useState(undefined)
    19   const [loading, setLoading] = useState(false)
     23  const [loading, setLoading] = useState(true)
    2024  useEffect(()=>{
    2125    getUser()
     
    2428  const getUser = () => {
    2529    setLoading(true)
    26     axios.get(env.api+'Users/authed',{headers:{Authorization:sessionStorage.getItem('Auth')}}).then(res=>{
    27       console.log(!res.data)
     30    axios.get(env.api+'Users/authed',{headers:{Authorization:localStorage.getItem('Auth')}}).then(res=>{
     31      console.log(res.data)
    2832      setUser(res.data)
    2933      setLoading(false)
     
    3236  const logout = () => {
    3337    setUser(undefined);
    34     sessionStorage.removeItem('Auth');
     38    localStorage.removeItem('Auth');
    3539    window.location.replace('/')
    3640  }
     
    4852            <Route path="/login" element={loading ? <Spin /> :  !user ? <Login setUser={setUser}/> :  <Navigate to="/dashboard" replace={true} />}/>
    4953            <Route path="/register" element={loading ? <Spin /> :  !user ? <Register setUser={setUser}/> :  <Navigate to="/dashboard" replace={true} />}/>
     54            <Route path="/reset-password" element={loading ? <Spin /> : <SendReset/>}/>
     55            <Route path="/reset" element={loading ? <Spin /> : <Reset/>}/>
     56            <Route path="/confirm" element={loading ? <Spin /> : <Confirm setUser={setUser} user={user}/>}/>
     57            <Route path="/confirm-email" element={loading ? <Spin /> : <SendConfirm setUser={setUser} user={user}/>}/>
    5058            <Route path="/" element={<FrontPage user={user} logout={logout}/>}/>
    5159          </Routes>
  • frontend/src/Auth/auth.js

    r899b19d ra26f6a1  
    44import { UserOutlined, LockOutlined } from '@ant-design/icons';
    55import axios from "axios";
    6 import './App.css'
    7 import env from "./env";
     6import '../App.css'
     7import env from "../env";
    88
    99const setAuthCookie = (token) => {
    10     sessionStorage.setItem('Auth','Bearer '+token)
     10    localStorage.setItem('Auth','Bearer '+token)
    1111}
    1212
     
    103103            setUser(res.data)
    104104            setLoading(false)
    105             history(res.data.isAdmin ? '/dashboard' : '/')
     105            history('/confirm-email')
    106106        })
    107107    }
  • frontend/src/Dashboard.js

    r899b19d ra26f6a1  
    1111    const history = useNavigate()
    1212    const logout = () => {
    13         sessionStorage.removeItem('Auth');
     13        localStorage.removeItem('Auth');
    1414        setUser(false)
    1515    }
  • frontend/src/Dashboard/Menu.js

    r899b19d ra26f6a1  
    2525    const addMenu = (data) => {
    2626        setLoadingSave(true);
    27         axios.post(env.api + 'Menu/',{...data,price: parseInt(data.price)}, { headers: {Authorization: sessionStorage.getItem('Auth')}
     27        axios.post(env.api + 'Menu/',{...data,price: parseInt(data.price)}, { headers: {Authorization: localStorage.getItem('Auth')}
    2828        }).then(res => {
    2929            notification['success']({
     
    4242
    4343    const deleteMenu = (id) => {
    44         axios.delete(env.api + 'Menu/'+id, {headers: {Authorization: sessionStorage.getItem('Auth')}
     44        axios.delete(env.api + 'Menu/'+id, {headers: {Authorization: localStorage.getItem('Auth')}
    4545        }).then(res => {
    4646            notification['success']({
  • frontend/src/Dashboard/Reservations.js

    r899b19d ra26f6a1  
    2626                    from: date[0].format('YYYY-MM-DDThh:mm:ss') + 'Z',
    2727                    to: date[1].format('YYYY-MM-DDThh:mm:ss') + 'Z'
    28                 }, headers: {Authorization: sessionStorage.getItem('Auth')}
     28                }, headers: {Authorization: localStorage.getItem('Auth')}
    2929            }).then(res => {
    3030                axios.get(env.api + 'Reservations/new', {
    31                      headers: {Authorization: sessionStorage.getItem('Auth')}
     31                     headers: {Authorization: localStorage.getItem('Auth')}
    3232                }).then(newres=>{
    3333                    setNewReservations(newres.data);
     
    5454            params: {
    5555                status: newStatus
    56             }, headers: {Authorization: sessionStorage.getItem('Auth')}
     56            }, headers: {Authorization: localStorage.getItem('Auth')}
    5757        }).then(res => {
    5858            getReservations()
     
    6868            params: {
    6969                tableId: ev.target.value == '' ? 0 : ev.target.value
    70             }, headers: {Authorization: sessionStorage.getItem('Auth')}
     70            }, headers: {Authorization: localStorage.getItem('Auth')}
    7171        }).then(res => {
    7272            console.log("success");
  • frontend/src/Dashboard/Restaurant.js

    r899b19d ra26f6a1  
    3737    const submitEdit = (form) => {
    3838        setLoadingSave(true);
    39         axios.put(env.api + 'Restaurants/',{...form}, { headers: {Authorization: sessionStorage.getItem('Auth')}
     39        axios.put(env.api + 'Restaurants/',{...form}, { headers: {Authorization: localStorage.getItem('Auth')}
    4040        }).then(res => {
    4141            notification['success']({
  • frontend/src/Dashboard/Review.js

    r899b19d ra26f6a1  
    11import React, {useEffect, useState} from 'react'
    2 import {Button, Card, Input, List, Rate, Spin, Tooltip} from "antd";
     2import {Button, Card, Input, List, notification, Rate, Spin, Tooltip} from "antd";
    33import axios from "axios";
    44import env from "../env";
     
    1212        setLoading(true);
    1313        getRestaurant()
     14        console.log(props.user)
    1415    },[])
    1516    useEffect(()=> {
     
    3637                {props.front ?
    3738                    (props.user?
    38                         <Button style={{float:'right'}} type={'primary'} onClick={()=>props.setVisible(true)}>Внеси оценка</Button>
     39                        <Button style={{float:'right'}} type={'primary'} onClick={()=>props.user.isConfirmed ? props.setVisible(true) : notification['error']({message: <p>Мора да го потврдите вашиот мејл за да оставите оценка. <a href={'/confirm-email'}>Потврдете го тука</a></p>})}>Внеси оценка</Button>
    3940                        : <Tooltip title={'Мора да се најавите за да оставите оценка'}><Button style={{float:'right'}} disabled type={'primary'} onClick={()=>props.setVisible(true)}>Внеси оценка</Button></Tooltip>)
    4041                    : ''
  • frontend/src/FrontPage.js

    r899b19d ra26f6a1  
    11import React, {useState, useEffect} from 'react'
    22import {Header} from "./Header";
    3 import {useNavigate} from "react-router-dom";
     3import {Link, useNavigate} from "react-router-dom";
    44import {Button, Card, DatePicker, Form, Image, Input, Modal, notification, Rate, Spin} from "antd";
    55import placeholderImage from '../src/Assets/placeholder.png'
     
    2626    },[])
    2727
     28    useEffect(()=>{
     29        if(user && !user.isConfirmed) {
     30            notification['warning']({
     31                message: <p>Вашиот емаил не е потврден. <a href={'/confirm-email'}>Потврдете го тука</a></p>
     32            });
     33        }
     34    },[user])
     35
    2836    const saveNewReview = data =>{
    2937        setSaveModalLoading(true)
    30         axios.post(env.api + 'Reviews',data,{ headers: {Authorization: sessionStorage.getItem('Auth')}}).then(res=>{
     38        axios.post(env.api + 'Reviews',data,{ headers: {Authorization: localStorage.getItem('Auth')}}).then(res=>{
    3139            setNewReviewModal(false)
    3240            setSaveModalLoading(false)
  • frontend/src/index.js

    r899b19d ra26f6a1  
    66const root = ReactDOM.createRoot(document.getElementById('root'));
    77root.render(
    8   <React.StrictMode>
    98    <App />
    10   </React.StrictMode>
    119);
  • resTools_backend/backend/Controllers/UsersController.cs

    r899b19d ra26f6a1  
    4141        }catch (Exception ex){ return null; }
    4242        User user = await _userService.GetById(userId);
    43         return new AuthenticateResponse() { Email=user.Email, Id = user.Id};
     43        return new AuthenticateResponse() { Email=user.Email, Id = user.Id, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed};
     44    }
     45
     46    [HttpPost("confirm")]
     47    public async Task ConfirmEmail()
     48    {
     49        int userId = 0;
     50        try
     51        {
     52            userId = (int)this.HttpContext.Items["User"];
     53        }
     54        catch (Exception ex) { return; }
     55        User user = await _userService.GetById(userId);
     56        await _userService.SendEmailConfirmation(user.Email);
     57    }
     58
     59    [HttpPost("reset")]
     60    public async Task ResetPassword(string email)
     61    {
     62        await _userService.SendPasswordReset(email);
     63    }
     64
     65    [HttpPost("confirmed")]
     66    public async Task ConfirmedEmail(string validityString)
     67    {
     68        int userId = 0;
     69        try
     70        {
     71            userId = (int)this.HttpContext.Items["User"];
     72        }
     73        catch (Exception ex) { return; }
     74        User user = await _userService.GetById(userId);
     75        await _userService.ConfirmEmail(user, validityString);
     76    }
     77
     78    [HttpPost("reseted")]
     79    public async Task ResetedPassword(string validityString, string newPassword)
     80    {
     81        await _userService.ResetPassword(validityString, newPassword);
    4482    }
    4583
  • resTools_backend/backend/DTOs/AuthenticateResponse.cs

    r899b19d ra26f6a1  
    1515    [JsonProperty]
    1616    public bool IsAdmin { get; set; }
     17    [JsonProperty]
     18    public bool IsConfirmed { get; set; }
    1719}
  • resTools_backend/backend/Entities/User.cs

    r899b19d ra26f6a1  
    99    public string Password { get; set; }
    1010    public bool IsAdmin { get; set; }
     11    public bool IsConfirmed { get; set; }
     12    public string? ConfirmationURL { get; set; }
     13    public DateTime? ConfirmationValidTo { get; set; }
     14    public string? PasswordResetURL { get; set; }
     15    public DateTime? PasswordResetValidTo { get; set; }
    1116    public virtual Restaurant Restaurant { get; set; }
    1217}
  • resTools_backend/backend/Migrations/DataContextModelSnapshot.cs

    r899b19d ra26f6a1  
    176176                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
    177177
     178                    b.Property<string>("ConfirmationURL")
     179                        .HasColumnType("text");
     180
     181                    b.Property<DateTime?>("ConfirmationValidTo")
     182                        .HasColumnType("timestamp with time zone");
     183
    178184                    b.Property<string>("Email")
    179185                        .IsRequired()
     
    183189                        .HasColumnType("boolean");
    184190
     191                    b.Property<bool>("IsConfirmed")
     192                        .HasColumnType("boolean");
     193
    185194                    b.Property<string>("Password")
    186195                        .IsRequired()
    187196                        .HasColumnType("text");
     197
     198                    b.Property<string>("PasswordResetURL")
     199                        .HasColumnType("text");
     200
     201                    b.Property<DateTime?>("PasswordResetValidTo")
     202                        .HasColumnType("timestamp with time zone");
    188203
    189204                    b.HasKey("Id");
  • resTools_backend/backend/Program.cs

    r899b19d ra26f6a1  
    11using backend.Data;
     2using backend.Email;
    23using backend.Helpers;
    34using backend.Services;
     
    5051builder.Services.AddScoped<ISmsService, SmsService>();
    5152
     53builder.Services.AddTransient<IEmailSender, EmailSender>();
     54
    5255builder.Services.AddDbContext<DataContext>(p => p.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
    5356
  • resTools_backend/backend/Services/UserService.cs

    r899b19d ra26f6a1  
    1 namespace backend.Services;
     1namespace backend.Services;
    22
    33using backend.Data;
    44using backend.DTOs;
     5using backend.Email;
    56using backend.Entities;
    67using backend.Helpers;
     
    1112using System.IdentityModel.Tokens.Jwt;
    1213using System.Security.Claims;
     14using System.Security.Cryptography;
     15using System.Text;
    1316
    1417public interface IUserService
     
    1720    Task<AuthenticateResponse> Register(CreateUserRequest req, bool isFirst);
    1821    Task<User> GetById(int id);
     22    Task SendEmailConfirmation(string email);
     23    Task SendPasswordReset(string email);
     24    Task ConfirmEmail(User user, string checkValid);
     25    Task ResetPassword(string checkValid, string password);
    1926}
    2027
     
    2330    private readonly AppSettings _appSettings;
    2431    private readonly DataContext _context = null;
     32    private readonly IEmailSender _emailSender;
    2533
    26     public UserService(IOptions<AppSettings> appSettings, DataContext context)
     34    public UserService(IOptions<AppSettings> appSettings, DataContext context, IEmailSender emailSender)
    2735    {
    2836        _appSettings = appSettings.Value;
    2937        _context = context;
     38        _emailSender = emailSender;
    3039    }
    3140
     
    4049        var token = generateJwtToken(user);
    4150
    42         return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin};
     51        return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = user.IsConfirmed};
     52    }
     53
     54    public async Task ConfirmEmail(User user, string checkValid)
     55    {
     56        if(user.ConfirmationURL != checkValid)
     57        {
     58            throw new Exception("Invalid check");
     59        }
     60        if(user.ConfirmationValidTo < DateTime.UtcNow)
     61        {
     62            throw new Exception("Link expired");
     63        }
     64
     65        user.IsConfirmed = true;
     66        _context.Users.Update(user);
     67        await _context.SaveChangesAsync();
    4368    }
    4469
     
    5075    public async Task<AuthenticateResponse> Register(CreateUserRequest req, bool isFirst)
    5176    {
    52         User user = new User() { Email = req.Email, Password = req.Password, IsAdmin = isFirst };
     77        User user = new User() { Email = req.Email, Password = req.Password, IsAdmin = isFirst, IsConfirmed = false };
    5378        await _context.Users.AddAsync(user);
    5479        await _context.SaveChangesAsync();
    5580        var token = generateJwtToken(user);
    56         return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin };
     81        return new AuthenticateResponse { Email = user.Email, Id = user.Id, Token = token, IsAdmin = user.IsAdmin, IsConfirmed = false };
     82    }
     83
     84    public async Task ResetPassword(string checkValid, string password)
     85    {
     86        var user = await _context.Users.Where(x => x.PasswordResetURL == checkValid).FirstOrDefaultAsync();
     87        if (user == null)
     88        {
     89            throw new Exception("Invalid check");
     90        }
     91        if (user.PasswordResetValidTo < DateTime.UtcNow)
     92        {
     93            throw new Exception("Link expired");
     94        }
     95
     96        user.Password = password;
     97        _context.Users.Update(user);
     98        await _context.SaveChangesAsync();
     99    }
     100
     101    public async Task SendEmailConfirmation(string email)
     102    {
     103        User user = await _context.Users.FirstOrDefaultAsync(x => x.Email == email);
     104        user.ConfirmationURL = Guid.NewGuid().ToString();
     105        user.ConfirmationValidTo = DateTime.UtcNow.AddHours(24);
     106        _context.Users.Update(user);
     107        await _context.SaveChangesAsync();
     108        await _emailSender.SendEmailAsync(
     109            "Потврдете го вашиот емаил",
     110            "Ве молиме кликнете на следниот линк за да го потврдите вашиот емаил: http://localhost:3000/confirm?id=" + user.ConfirmationURL,
     111            email);
     112    }
     113
     114    public async Task SendPasswordReset(string email)
     115    {
     116        User user = await _context.Users.FirstOrDefaultAsync(x => x.Email == email);
     117        user.PasswordResetURL = Guid.NewGuid().ToString();
     118        user.PasswordResetValidTo = DateTime.UtcNow.AddHours(24);
     119        _context.Users.Update(user);
     120        await _context.SaveChangesAsync();
     121        await _emailSender.SendEmailAsync(
     122           "Ресетирајте ја лозинката",
     123           "Ве молиме кликнете на следниот линк за да ја ресетирате лозинката: http://localhost:3000/reset?id=" + user.PasswordResetURL,
     124           email);
    57125    }
    58126
     
    71139        return tokenHandler.WriteToken(token);
    72140    }
     141   
     142    private string sha256Hash(String value)
     143    {
     144        using (SHA256 hash = SHA256.Create())
     145        {
     146            return String.Concat(hash
     147              .ComputeHash(Encoding.UTF8.GetBytes(value))
     148              .Select(item => item.ToString("x2")));
     149        }
     150    }
    73151}
  • resTools_backend/backend/backend.csproj

    r899b19d ra26f6a1  
    1 <Project Sdk="Microsoft.NET.Sdk.Web">
     1<Project Sdk="Microsoft.NET.Sdk.Web">
    22
    33  <PropertyGroup>
     
    2222    <PackageReference Include="Npgsql" Version="6.0.3" />
    2323    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.3" />
     24    <PackageReference Include="SendGrid" Version="9.28.0" />
    2425    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
    2526    <PackageReference Include="Swashbuckle.Core" Version="5.6.0" />
Note: See TracChangeset for help on using the changeset viewer.