-
-
Save aasutossh/4ea37f1778b143f3e1c8ce768da1fe13 to your computer and use it in GitHub Desktop.
Revisions
-
kalafut revised this gist
Jan 9, 2024 . 2 changed files with 9 additions and 17 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -97,13 +97,13 @@ func main() { } // validate expiration if record.GetDateTime("expiration").Time().Before(time.Now()) { app.Dao().DeleteRecord(record) return unauthorizedErr } // validate otp if !security.Equal(record.GetString("otp"), data.OTP) { attempts := record.GetInt("attempts") + 1 if attempts > 3 { app.Dao().DeleteRecord(record) This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,32 +3,24 @@ // Step 1 // Start the auth with an email final resp = await pb.send("/otp-auth", method: "POST", body: { "email": emailController.text, }); // Save this token for the next step final otpToken = resp['verifyToken']; // // ... email with OTP is received and we want to complete the auth... // // Step 2 final resp = await pb.send("/otp-verify", method: "POST", body: { "verifyToken": widget.otpToken, "otp": otpController.text, }); // Complete the process by updating the auth store. final auth = RecordAuth.fromJson(resp); pb.authStore.save(auth.token, auth.record); -
kalafut revised this gist
Jan 8, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -29,6 +29,6 @@ final resp = await post(Uri.parse("$pburl/otp-verify"), final data = jsonDecode(resp.body); // Complete the process by updating the auth store. final auth = RecordAuth.fromJson(data); pb.authStore.save(auth.token, auth.record); -
kalafut revised this gist
Jan 8, 2024 . No changes.There are no files selected for viewing
-
kalafut revised this gist
Jan 8, 2024 . No changes.There are no files selected for viewing
-
kalafut revised this gist
Jan 8, 2024 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -103,7 +103,7 @@ func main() { } // validate otp if record.GetString("otp") != data.OTP { attempts := record.GetInt("attempts") + 1 if attempts > 3 { app.Dao().DeleteRecord(record) -
kalafut revised this gist
Jan 8, 2024 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,6 +28,7 @@ final resp = await post(Uri.parse("$pburl/otp-verify"), })); final data = jsonDecode(resp.body); // Complete the process but updating the auth store. final auth = RecordAuth.fromJson(data); pb.authStore.save(auth.token, auth.record); -
kalafut revised this gist
Jan 8, 2024 . 1 changed file with 4 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -25,10 +25,10 @@ func main() { // user - relation to the user record // expiration - the expiration date of the token // attempts - the number of failed attempts to verify the token // // The user requests login and receives an opaque verifyToken in response, // and the OTP is sent to the user's email. The user then submits the OTP // and verifyToken to the server for verification. app.OnBeforeServe().Add(func(e *core.ServeEvent) error { // TODO: add a cron job to delete expired otp_auth records -
kalafut renamed this gist
Jan 8, 2024 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
kalafut created this gist
Jan 8, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,33 @@ // Example Flutter snippets to interact with the OTP auth // Step 1 // Start the auth with an email final resp = await post(Uri.parse("$pburl/otp-auth"), body: { "email": emailController.text, }); // Save this token for the next step final verifyToken = jsonDecode(resp.body)['verifyToken']; // // ... email with OTP is received and we want to complete the auth... // // Step 2 final headers = <String, String>{ 'Content-Type': 'application/json', }; final resp = await post(Uri.parse("$pburl/otp-verify"), headers: headers, body: jsonEncode({ "verifyToken": widget.otpToken, // saved from Step 1 "otp": otpController.text, // code entered by the user })); final data = jsonDecode(resp.body); final auth = RecordAuth.fromJson(data); // from Pocketbase SDK pb.authStore.save(auth.token, auth.record); This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,143 @@ package main import ( "log" "time" "github.com/labstack/echo/v5" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/apis" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/models" "github.com/pocketbase/pocketbase/tools/security" "github.com/pocketbase/pocketbase/tools/types" ) var unauthorizedErr = apis.NewUnauthorizedError("Invalid or expired OTP token", nil) var badEmailErr = apis.NewBadRequestError("Invalid or unknown email", nil) func main() { app := pocketbase.New() // To support OTP authentication, a new collection (otp_auth) was created with the following fields: // // otp - the generated OTP token // user - relation to the user record // expiration - the expiration date of the token // attempts - the number of failed attempts to verify the token // // The user requests login and receives an opaque verifyToken in response, // and the OTP is sent to the user's email. The user then submits the OTP // and verifyToken to the server for verification. app.OnBeforeServe().Add(func(e *core.ServeEvent) error { // TODO: add a cron job to delete expired otp_auth records // This is not a security related and can be done occasionally. // Step 1: User requests an OTP token // // The otp-auth endpoint is used to generate an otp_auth record. If the user's email // is found, the OTP is stored, emailed, and verifyToken (this record's ID) is returned // to the caller. e.Router.POST("/otp-auth", func(c echo.Context) error { data := apis.RequestInfo(c).Data email, ok := data["email"].(string) if !ok { return badEmailErr } user, err := app.Dao().FindAuthRecordByEmail("users", email) if err != nil { return badEmailErr } collection, err := app.Dao().FindCollectionByNameOrId("otp_auth") if err != nil { log.Println("find collection error", err) return apis.NewBadRequestError("", nil) } record := models.NewRecord(collection) record.Set("user", user.GetId()) record.Set("expiration", time.Now().Add(time.Minute*10)) otp := security.RandomStringWithAlphabet(6, "0123456789") record.Set("otp", otp) log.Println("otp", otp) // For testing purposes. Normally, you would send this to the user's email. if err := app.Dao().SaveRecord(record); err != nil { log.Println("save error", err) return apis.NewBadRequestError("", nil) } return c.JSON(200, echo.Map{ "verifyToken": record.GetId(), }) }) // Step 2: User submits the OTP token for verification // // The otp-verify endpoint is used to verify the OTP token. If the token is valid, // the user is authenticated and the standard RecordAuthResponse is returned. e.Router.POST("/otp-verify", func(c echo.Context) error { data := struct { VerifyToken string `json:"verifyToken"` OTP string `json:"otp"` }{} if err := c.Bind(&data); err != nil { log.Println("bind error", err) return unauthorizedErr } record, err := app.Dao().FindRecordById("otp_auth", data.VerifyToken) if err != nil { return unauthorizedErr } // validate expiration if record.Get("expiration").(types.DateTime).Time().Before(time.Now()) { app.Dao().DeleteRecord(record) return unauthorizedErr } // validate otp if record.Get("otp").(string) != data.OTP { attempts := record.GetInt("attempts") + 1 if attempts > 3 { app.Dao().DeleteRecord(record) return unauthorizedErr } record.Set("attempts", attempts) if err := app.Dao().SaveRecord(record); err != nil { log.Println("save error", err) } return unauthorizedErr } // At this point the OTP record is consumed and should be deleted defer app.Dao().DeleteRecord(record) if err := app.Dao().ExpandRecord(record, []string{"user"}, nil); len(err) > 0 { log.Println("expand error", err) return unauthorizedErr } user := record.ExpandedOne("user") if user == nil { return unauthorizedErr } return apis.RecordAuthResponse(app, c, user, nil) }) return nil }) if err := app.Start(); err != nil { log.Fatal(err) } }