all repos

onasty @ bf8dc57251dbc6827d93ef4bb0e5461f6084a099

a one-time notes service

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

Olexandr Smirnov Olexandr Smirnov
ss2316544@gmail.com
feat(api): change email (#191)..., 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
		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
type resendVerificationEmailRequest struct {
101
	Email string `json:"email"`
102
}
103
104
func (a *APIV1) resendVerificationEmailHandler(c *gin.Context) {
105
	var req resendVerificationEmailRequest
106
	if err := c.ShouldBindJSON(&req); err != nil {
107
		newError(c, http.StatusBadRequest, "invalid request")
108
		return
109
	}
110
111
	if err := a.usersrv.ResendVerificationEmail(
112
		c.Request.Context(),
113
		dtos.ResendVerificationEmail{
114
			Email: req.Email,
115
		}); err != nil {
116
		errorResponse(c, err)
117
		return
118
	}
119
120
	c.Status(http.StatusOK)
121
}
122
123
type requestResetPasswordRequest struct {
124
	Email string `json:"email"`
125
}
126
127
func (a *APIV1) requestResetPasswordHandler(c *gin.Context) {
128
	var req requestResetPasswordRequest
129
	if err := c.ShouldBindJSON(&req); err != nil {
130
		newError(c, http.StatusBadRequest, "invalid request")
131
		return
132
	}
133
134
	if err := a.usersrv.RequestPasswordReset(c.Request.Context(), dtos.RequestResetPassword{
135
		Email: req.Email,
136
	}); err != nil {
137
		errorResponse(c, err)
138
		return
139
	}
140
141
	c.Status(http.StatusOK)
142
}
143
144
type resetPasswordRequest struct {
145
	Password string `json:"password"`
146
}
147
148
func (a *APIV1) resetPasswordHandler(c *gin.Context) {
149
	var req resetPasswordRequest
150
	if err := c.ShouldBindJSON(&req); err != nil {
151
		newError(c, http.StatusBadRequest, "invalid request")
152
		return
153
	}
154
155
	if err := a.usersrv.ResetPassword(
156
		c.Request.Context(),
157
		dtos.ResetPassword{
158
			Token:       c.Param("token"),
159
			NewPassword: req.Password,
160
		},
161
	); err != nil {
162
		errorResponse(c, err)
163
		return
164
	}
165
166
	c.Status(http.StatusOK)
167
}
168
169
type logoutRequest struct {
170
	RefreshToken string `json:"refresh_token"`
171
}
172
173
func (a *APIV1) logOutHandler(c *gin.Context) {
174
	var req logoutRequest
175
	if err := c.ShouldBindJSON(&req); err != nil {
176
		newError(c, http.StatusBadRequest, "invalid request")
177
		return
178
	}
179
180
	if err := a.usersrv.Logout(c.Request.Context(), a.getUserID(c), req.RefreshToken); err != nil {
181
		errorResponse(c, err)
182
		return
183
	}
184
185
	c.Status(http.StatusNoContent)
186
}
187
188
func (a *APIV1) logOutAllHandler(c *gin.Context) {
189
	if err := a.usersrv.LogoutAll(c.Request.Context(), a.getUserID(c)); err != nil {
190
		errorResponse(c, err)
191
		return
192
	}
193
194
	c.Status(http.StatusNoContent)
195
}
196
197
type changePasswordRequest struct {
198
	CurrentPassword string `json:"current_password"`
199
	NewPassword     string `json:"new_password"`
200
}
201
202
func (a *APIV1) changePasswordHandler(c *gin.Context) {
203
	var req changePasswordRequest
204
	if err := c.ShouldBindJSON(&req); err != nil {
205
		newError(c, http.StatusBadRequest, "invalid request")
206
		return
207
	}
208
209
	if err := a.usersrv.ChangePassword(
210
		c.Request.Context(),
211
		a.getUserID(c),
212
		dtos.ChangeUserPassword{
213
			CurrentPassword: req.CurrentPassword,
214
			NewPassword:     req.NewPassword,
215
		}); err != nil {
216
		errorResponse(c, err)
217
		return
218
	}
219
220
	c.Status(http.StatusOK)
221
}
222
223
type changeEmailRequest struct {
224
	NewEmail string `json:"new_email"`
225
}
226
227
func (a *APIV1) requestEmailChangeHandler(c *gin.Context) {
228
	var req changeEmailRequest
229
	if err := c.ShouldBindJSON(&req); err != nil {
230
		newError(c, http.StatusBadRequest, "invalid request")
231
		return
232
	}
233
234
	if err := a.usersrv.RequestEmailChange(
235
		c.Request.Context(),
236
		a.getUserID(c),
237
		dtos.ChangeEmail{
238
			NewEmail: req.NewEmail,
239
		}); err != nil {
240
		errorResponse(c, err)
241
		return
242
	}
243
244
	c.Status(http.StatusOK)
245
}
246
247
func (a *APIV1) changeEmailHandler(c *gin.Context) {
248
	if err := a.usersrv.ChangeEmail(c.Request.Context(), c.Param("token")); err != nil {
249
		errorResponse(c, err)
250
		return
251
	}
252
253
	c.String(http.StatusOK, "email changed")
254
}
255
256
const oatuhStateCookie = "oauth_state"
257
258
func (a *APIV1) oauthLoginHandler(c *gin.Context) {
259
	redirectInfo, err := a.usersrv.GetOAuthURL(c.Param("provider"))
260
	if err != nil {
261
		errorResponse(c, err)
262
		return
263
	}
264
265
	c.SetCookie(
266
		oatuhStateCookie,
267
		redirectInfo.State,
268
		int(time.Minute.Seconds()),
269
		"/",
270
		a.domain,
271
		!a.env.IsDevMode(),
272
		true,
273
	)
274
275
	c.Redirect(http.StatusSeeOther, redirectInfo.URL)
276
}
277
278
func (a *APIV1) oauthCallbackHandler(c *gin.Context) {
279
	state := c.Query("state")
280
	storedState, err := c.Cookie(oatuhStateCookie)
281
	if err != nil || state != storedState {
282
		newError(c, http.StatusBadRequest, "invalid oauth state")
283
		return
284
	}
285
286
	tokens, err := a.usersrv.HandleOAuthLogin(
287
		c.Request.Context(),
288
		c.Param("provider"),
289
		c.Query("code"),
290
	)
291
	if err != nil {
292
		errorResponse(c, err)
293
		return
294
	}
295
296
	c.JSON(http.StatusOK, signInResponse{
297
		AccessToken:  tokens.Access,
298
		RefreshToken: tokens.Refresh,
299
	})
300
}
301
302
type getMeResponse struct {
303
	Email        string    `json:"email"`
304
	CreatedAt    time.Time `json:"created_at"`
305
	LastLoginAt  time.Time `json:"last_login_at"`
306
	NotesCreated int       `json:"notes_created"`
307
}
308
309
func (a *APIV1) getMeHandler(c *gin.Context) {
310
	uinfo, err := a.usersrv.GetUserInfo(c.Request.Context(), a.getUserID(c))
311
	if err != nil {
312
		errorResponse(c, err)
313
		return
314
	}
315
316
	c.JSON(http.StatusOK, getMeResponse{
317
		Email:        uinfo.Email,
318
		CreatedAt:    uinfo.CreatedAt,
319
		LastLoginAt:  uinfo.LastLoginAt,
320
		NotesCreated: uinfo.NotesCreated,
321
	})
322
}