all repos

onasty @ 62e4dde75f5cca100c807673176826e5f120c86d

a one-time notes service

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

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat: /me (#132)..., 12 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
		newError(c, http.StatusBadRequest, "invalid request")
20
		return
21
	}
22
23
	if _, err := a.usersrv.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
		newError(c, http.StatusBadRequest, "invalid request")
50
		return
51
	}
52
53
	toks, err := a.usersrv.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
		newError(c, http.StatusBadRequest, "invalid request")
76
		return
77
	}
78
79
	toks, err := a.usersrv.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
func (a *APIV1) verifyHandler(c *gin.Context) {
92
	if err := a.usersrv.Verify(c.Request.Context(), c.Param("token")); err != nil {
93
		errorResponse(c, err)
94
		return
95
	}
96
97
	c.String(http.StatusOK, "email verified")
98
}
99
100
func (a *APIV1) resendVerificationEmailHandler(c *gin.Context) {
101
	var req signInRequest
102
	if err := c.ShouldBindJSON(&req); err != nil {
103
		newError(c, http.StatusBadRequest, "invalid request")
104
		return
105
	}
106
107
	if err := a.usersrv.ResendVerificationEmail(
108
		c.Request.Context(),
109
		dtos.SignIn{
110
			Email:    req.Email,
111
			Password: req.Password,
112
		}); err != nil {
113
		errorResponse(c, err)
114
		return
115
	}
116
117
	c.Status(http.StatusOK)
118
}
119
120
type requestResetPasswordRequest struct {
121
	Email string `json:"email"`
122
}
123
124
func (a *APIV1) requestResetPasswordHandler(c *gin.Context) {
125
	var req requestResetPasswordRequest
126
	if err := c.ShouldBindJSON(&req); err != nil {
127
		newError(c, http.StatusBadRequest, "invalid request")
128
		return
129
	}
130
131
	if err := a.usersrv.RequestPasswordReset(c.Request.Context(), dtos.RequestResetPassword{
132
		Email: req.Email,
133
	}); err != nil {
134
		errorResponse(c, err)
135
		return
136
	}
137
138
	c.Status(http.StatusOK)
139
}
140
141
type resetPasswordRequest struct {
142
	Password string `json:"password"`
143
}
144
145
func (a *APIV1) resetPasswordHandler(c *gin.Context) {
146
	var req resetPasswordRequest
147
	if err := c.ShouldBindJSON(&req); err != nil {
148
		newError(c, http.StatusBadRequest, "invalid request")
149
		return
150
	}
151
152
	if err := a.usersrv.ResetPassword(
153
		c.Request.Context(),
154
		dtos.ResetPassword{
155
			Token:       c.Param("token"),
156
			NewPassword: req.Password,
157
		},
158
	); err != nil {
159
		errorResponse(c, err)
160
		return
161
	}
162
163
	c.Status(http.StatusOK)
164
}
165
166
type logoutRequest struct {
167
	RefreshToken string `json:"refresh_token"`
168
}
169
170
func (a *APIV1) logOutHandler(c *gin.Context) {
171
	var req logoutRequest
172
	if err := c.ShouldBindJSON(&req); err != nil {
173
		newError(c, http.StatusBadRequest, "invalid request")
174
		return
175
	}
176
177
	if err := a.usersrv.Logout(c.Request.Context(), a.getUserID(c), req.RefreshToken); err != nil {
178
		errorResponse(c, err)
179
		return
180
	}
181
182
	c.Status(http.StatusNoContent)
183
}
184
185
func (a *APIV1) logOutAllHandler(c *gin.Context) {
186
	if err := a.usersrv.LogoutAll(c.Request.Context(), a.getUserID(c)); err != nil {
187
		errorResponse(c, err)
188
		return
189
	}
190
191
	c.Status(http.StatusNoContent)
192
}
193
194
type changePasswordRequest struct {
195
	CurrentPassword string `json:"current_password"`
196
	NewPassword     string `json:"new_password"`
197
}
198
199
func (a *APIV1) changePasswordHandler(c *gin.Context) {
200
	var req changePasswordRequest
201
	if err := c.ShouldBindJSON(&req); err != nil {
202
		newError(c, http.StatusBadRequest, "invalid request")
203
		return
204
	}
205
206
	if err := a.usersrv.ChangePassword(
207
		c.Request.Context(),
208
		a.getUserID(c),
209
		dtos.ChangeUserPassword{
210
			CurrentPassword: req.CurrentPassword,
211
			NewPassword:     req.NewPassword,
212
		}); err != nil {
213
		errorResponse(c, err)
214
		return
215
	}
216
217
	c.Status(http.StatusOK)
218
}
219
220
const oatuhStateCookie = "oauth_state"
221
222
func (a *APIV1) oauthLoginHandler(c *gin.Context) {
223
	redirectInfo, err := a.usersrv.GetOAuthURL(c.Param("provider"))
224
	if err != nil {
225
		errorResponse(c, err)
226
		return
227
	}
228
229
	c.SetCookie(
230
		oatuhStateCookie,
231
		redirectInfo.State,
232
		int(time.Minute.Seconds()),
233
		"/",
234
		a.domain,
235
		!a.env.IsDevMode(),
236
		true,
237
	)
238
239
	c.Redirect(http.StatusSeeOther, redirectInfo.URL)
240
}
241
242
func (a *APIV1) oauthCallbackHandler(c *gin.Context) {
243
	state := c.Query("state")
244
	storedState, err := c.Cookie(oatuhStateCookie)
245
	if err != nil || state != storedState {
246
		newError(c, http.StatusBadRequest, "invalid oauth state")
247
		return
248
	}
249
250
	tokens, err := a.usersrv.HandleOAuthLogin(
251
		c.Request.Context(),
252
		c.Param("provider"),
253
		c.Query("code"),
254
	)
255
	if err != nil {
256
		errorResponse(c, err)
257
		return
258
	}
259
260
	c.JSON(http.StatusOK, signInResponse{
261
		AccessToken:  tokens.Access,
262
		RefreshToken: tokens.Refresh,
263
	})
264
}
265
266
type getMeResponse struct {
267
	Email     string    `json:"email"`
268
	CreatedAt time.Time `json:"created_at"`
269
}
270
271
func (a *APIV1) getMeHandler(c *gin.Context) {
272
	uinfo, err := a.usersrv.GetUserInfo(c.Request.Context(), a.getUserID(c))
273
	if err != nil {
274
		errorResponse(c, err)
275
		return
276
	}
277
278
	c.JSON(http.StatusOK, getMeResponse{
279
		Email:     uinfo.Email,
280
		CreatedAt: uinfo.CreatedAt,
281
	})
282
}