all repos

onasty @ 10fe031c01c55b5471677cc573d3a83335ce5b05

a one-time notes service

onasty/internal/transport/http/apiv1/auth.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
refactor: common response for invalid bodies (#203), 9 months ago
1
package apiv1
2
3
import (
4
	"net/http"
5
	"time"
6
7
	"github.com/gin-gonic/gin"
8
	"github.com/olexsmir/onasty/internal/dtos"
9
)
10
11
type signUpRequest struct {
12
	Email    string `json:"email"`
13
	Password string `json:"password"`
14
}
15
16
func (a *APIV1) signUpHandler(c *gin.Context) {
17
	var req signUpRequest
18
	if err := c.ShouldBindJSON(&req); err != nil {
19
		invalidRequest(c)
20
		return
21
	}
22
23
	if err := a.authsrv.SignUp(c.Request.Context(), dtos.SignUp{
24
		Email:       req.Email,
25
		Password:    req.Password,
26
		CreatedAt:   time.Now(),
27
		LastLoginAt: time.Now(),
28
	}); err != nil {
29
		errorResponse(c, err)
30
		return
31
	}
32
33
	c.Status(http.StatusCreated)
34
}
35
36
type signInRequest struct {
37
	Email    string `json:"email"`
38
	Password string `json:"password"`
39
}
40
41
type signInResponse struct {
42
	AccessToken  string `json:"access_token"`
43
	RefreshToken string `json:"refresh_token"`
44
}
45
46
func (a *APIV1) signInHandler(c *gin.Context) {
47
	var req signInRequest
48
	if err := c.ShouldBindJSON(&req); err != nil {
49
		invalidRequest(c)
50
		return
51
	}
52
53
	toks, err := a.authsrv.SignIn(c.Request.Context(), dtos.SignIn{
54
		Email:    req.Email,
55
		Password: req.Password,
56
	})
57
	if err != nil {
58
		errorResponse(c, err)
59
		return
60
	}
61
62
	c.JSON(http.StatusOK, signInResponse{
63
		AccessToken:  toks.Access,
64
		RefreshToken: toks.Refresh,
65
	})
66
}
67
68
type refreshTokenRequest struct {
69
	RefreshToken string `json:"refresh_token"`
70
}
71
72
func (a *APIV1) refreshTokensHandler(c *gin.Context) {
73
	var req refreshTokenRequest
74
	if err := c.ShouldBindJSON(&req); err != nil {
75
		invalidRequest(c)
76
		return
77
	}
78
79
	toks, err := a.authsrv.RefreshTokens(c.Request.Context(), req.RefreshToken)
80
	if err != nil {
81
		errorResponse(c, err)
82
		return
83
	}
84
85
	c.JSON(http.StatusOK, signInResponse{
86
		AccessToken:  toks.Access,
87
		RefreshToken: toks.Refresh,
88
	})
89
}
90
91
type logoutRequest struct {
92
	RefreshToken string `json:"refresh_token"`
93
}
94
95
func (a *APIV1) logOutHandler(c *gin.Context) {
96
	var req logoutRequest
97
	if err := c.ShouldBindJSON(&req); err != nil {
98
		invalidRequest(c)
99
		return
100
	}
101
102
	if err := a.authsrv.Logout(
103
		c.Request.Context(),
104
		a.getUserID(c),
105
		req.RefreshToken,
106
	); err != nil {
107
		errorResponse(c, err)
108
		return
109
	}
110
111
	c.Status(http.StatusNoContent)
112
}
113
114
func (a *APIV1) logOutAllHandler(c *gin.Context) {
115
	if err := a.authsrv.LogoutAll(c.Request.Context(), a.getUserID(c)); err != nil {
116
		errorResponse(c, err)
117
		return
118
	}
119
120
	c.Status(http.StatusNoContent)
121
}
122
123
const oatuhStateCookie = "oauth_state"
124
125
func (a *APIV1) oauthLoginHandler(c *gin.Context) {
126
	redirectInfo, err := a.authsrv.GetOAuthURL(c.Param("provider"))
127
	if err != nil {
128
		errorResponse(c, err)
129
		return
130
	}
131
132
	c.SetCookie(
133
		oatuhStateCookie,
134
		redirectInfo.State,
135
		int(time.Minute.Seconds()),
136
		"/",
137
		a.domain,
138
		!a.env.IsDevMode(),
139
		true,
140
	)
141
142
	c.Redirect(http.StatusSeeOther, redirectInfo.URL)
143
}
144
145
func (a *APIV1) oauthCallbackHandler(c *gin.Context) {
146
	state := c.Query("state")
147
	storedState, err := c.Cookie(oatuhStateCookie)
148
	if err != nil || state != storedState {
149
		newError(c, http.StatusBadRequest, "invalid oauth state")
150
		return
151
	}
152
153
	tokens, err := a.authsrv.HandleOAuthLogin(
154
		c.Request.Context(),
155
		c.Param("provider"),
156
		c.Query("code"),
157
	)
158
	if err != nil {
159
		errorResponse(c, err)
160
		return
161
	}
162
163
	c.JSON(http.StatusOK, signInResponse{
164
		AccessToken:  tokens.Access,
165
		RefreshToken: tokens.Refresh,
166
	})
167
}