I am sending password reset link to my users using cognito CustomMessage_ForgotPassword event. Here is my handler code.
func handler(ctx context.Context, event *events.CognitoEventUserPoolsCustomMessage) (*events.CognitoEventUserPoolsCustomMessage,error)
{
resetURL := fmt.Sprintf("%s/login/recover", os.Getenv("DOMAIN_URL"))
var customMessageForgotPassword = fmt.Sprintf(
`<style>
p {
display: block;
}
</style>
<div tabindex="-1"><p>Hello,</p>
<p> Click on link below to reset your password.</p>
<p> Reset Password </p>
<p>Thanks</p>
</div>`,
resetURL,
event.Request.CodeParameter, event.Request.UserAttributes["Email"])
if event.TriggerSource == "CustomMessage_ForgotPassword" {
event.Response.EmailMessage = customMessageForgotPassword
event.Response.EmailSubject = "Reset Your Password"
}
return event, nil
}
Here is the definition of CognitoEventUserPoolsCustomMessageRequest in cognito.go file.
type CognitoEventUserPoolsCustomMessage struct {
CognitoEventUserPoolsHeader
Request CognitoEventUserPoolsCustomMessageRequest `json:"request"`
Response CognitoEventUserPoolsCustomMessageResponse `json:"response"`
}
type CognitoEventUserPoolsCustomMessageRequest struct {
UserAttributes map[string]interface{} `json:"userAttributes"`
CodeParameter string `json:"codeParameter"`
UsernameParameter string `json:"usernameParameter"`
ClientMetadata map[string]string `json:"clientMetadata"`
}
In email I am getting Reset password url parameters like Domain_URL/login/recover?code=13578&email=%!s()
Tried with parameters like UsernameParameter and some other solutions. But always getting nil here.
Any solution to this problem?
Related
I am creating an auth flow for my mobile app. I received an error: "Cannot find user in scope" on "self.userSession = user" & ".document(.user.uid)" I'm not sure how to resolve this error. Any help would be appreciated!
`
import SwiftUI
import Firebase
import FirebaseCore
import FirebaseAuth
import FirebaseStorage
import FirebaseFirestore
import FirebaseFirestoreSwift
class AuthViewModel: ObservableObject {
#Published var userSession: FirebaseAuth.User?
init() {
self.userSession = Auth.auth().currentUser
print("DEBUG: User session is \(self.userSession?.uid)")
}
func login(withEmail email: String, password: String) {
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to sign in with error \(error.localizedDescription)")
return
}
guard let user = result?.user else { return }
self.userSession = user
print("DEBUG: Did log user in..")
}
}
func register(withEmail email: String, password: String, fullname: String) {
Auth.auth().createUser(withEmail: email, password: password) { result, error in
if let error = error {
print("DEBUG: Failed to register with error \(error.localizedDescription)")
return
}
print("DEBUG: Registered user successfully.")
print("DEBUG: User is \(self.userSession)")
let data = ["email": email,
"fullname": fullname,
"uid": user.uid]
Firestore.firestore().collection("users")
.document(user.uid)
.setData(data) { _ in
print("DEBUG: Did upload user data..")
}
}
}
func signOut() {
// sets user session to nil so we show login view
userSession = nil
// signs user out on server
try? Auth.auth().signOut()
}
}
`
I have tried moving the declaration up on to the viewmodel but that caused another error: "cannot find result in scope".
You should use an auth state change listener to save user session as mentioned in the Firebase documentation. You should also not initialize the auth object every time in every function.
import FirebaseAuth
class AuthViewModel: Observable Object {
private let auth: Auth = Auth.auth() // You can also use dependency injection
#Published var userSession: User?
private var authStatusHandler: AuthStateDidChangeListenerHandle?
func addAuthStatusListener() {
if let handle = authStatusHandler {
auth.removeStateDidChangeListener(handle)
}
authStatusHandler = auth.addStateDidChangeListener { _, user in
self.userSession = user
}
}
...
}
In the register function, you try to access the "user" variable which is defined in the login function. This variable is out of the scope of the register function. You should use the userSession in the class for the register function.
func register(withEmail email: String, password: String, fullname: String) {
...
let data = ["email": email,
"fullname": fullname,
"uid": userSession.uid] // <- Here
Firestore.firestore().collection("users")
.document(userSession.uid) // <- Here
.setData(data) { _ in
print("DEBUG: Did upload user data..")
}
...
}
In one view, I have this func to signUp and get userName
func signUp() {
if self.email != "" {
if self.password == self.repass {
session.signUp(email: email, password: password) { (result, error) in
if error != nil {
self.error = error!.localizedDescription
//self.error = "A senha deve ter 6 caracteres ou mais"
}
else{
self.email = ""
self.password = ""
let db = Firestore.firestore()
db.collection("Users").document(result!.user.uid).setData(["userName": userName]) { (error) in
if error != nil{
}
}
}
}
}
else{
self.error = "Senhas não coincidem"
}
}
else{
self.error = "Complete todos espaços"
}
}
In other view, I want to get the userName the user typed ad display it, but for this I need to link both views to get access to func SignUp. How to do it?
EDIT:
Based on the answer I was given, I did the corresponding changes:
SessionStore:
#Published var session: User? = nil
var handle: AuthStateDidChangeListenerHandle?
#Published var userName: String = ""
func listen() {
handle = Auth.auth().addStateDidChangeListener({ [self] (auth, user) in
if let user = user {
self.session = User(uid: user.uid, email: user.email!, userName: userName)
}
else {
self.session = nil
}
})
}
struct User {
var uid:String
var email:String
var userName: String
}
HomeView ----> var userName: User
Text(userName.userName)
explaining how my code works: the #main view is MedAppApp: App. From there, the code goes to ContentView. if session.session != nil, TransitionView, else, SignView. Continuing from SignView, there I have both SignInView and SignUpView views, which they have Button(action: signIn) and Button(action: Signup), respectively. Continuing from TransitionView, there is where I have:
struct TransitionView: View {
#StateObject var LocationModel = LocationViewModel()
#State var userName = User(uid: "", email: "", userName: "")
var body: some View {
HomeView(LocationModel: LocationModel, userName: userName)
}
}
#State var userName =... and userName: userName needed to be added
The app launches, I can signUp with username (and its stored in Firestore with uid as document), but the userName isn't displayed. I think the problem must be in User(uid: "", email: "", password: ""). I didn't know what to add there.
By the way, Im thinking about maintaining the code and then get the userName by using the document id (that already is the user uid) so I can access it. Don't have a final answer yet, but Im working on it.
EDIT2:
I added #StateObject var session = SessionStore()
Tried to use:
#State var userName = User(uid: SessionStore().session?.uid ?? "", email: SessionStore().session?.email ?? "", userName: SessionStore().session?.userName ?? "")
Still doesn't work
Your question is too open-ended, but I'll provide an answer that hopefully will set you up on the right direction.
First, in SwiftUI, separate the concept of a view (i.e. how things are arranged on the screen) from the concept of data that drives the views. What I mean by that is, if you need some data, like userName, the code that obtains it shouldn't know or care about which view will make use of it. And Views, shouldn't care how the data is obtained.
Second, in SwiftUI, views should not be thought of as "entities" (like in UIKit) with their own life cycles. They are just declarative statements that define how the UI is arranged.
For example, suppose you have this (fake) class that signs the user in, and when signed-in, populates the userName property. The instance of this class is a view model.
struct User {
let userName: String
}
class Session: ObservableObject {
#Published var user: User? = nil
func signIn(_ completion: (User) -> ()) {
DispatchQueue.main.asyncAfter(.now() + 0.5) {
self.user = User(userName: "Mateus") // fake sign-in
completion(self.user) // not really used in this example
}
}
}
#Published and ObservableObject in combination are how SwiftUI views know to react to changes to this object. Read more about them, if you don't know what they do.
Next, design a root view that decides what inner view to display depending on whether the user is signed in or not:
struct RootView: View {
#StateObject var session = Session()
var body: some View {
VStack() {
if let user = session.user {
ContentView(user: user) // view is conditionally rendered
} else {
Button("sign in") { self.signIn() }
}
}
}
func signIn() {
self.session.signIn() {
print("signed in...!")
}
}
}
#StateObject manages the life cycle of the view model for the view.
As you see above, ContentView will only be rendered when session.user is not nil (i.e. when user has signed in), so the view is driven by changes in data. I did not need to create a ContentView instance anywhere ahead of time.
Then ContentView is straightforward:
struct ContentView: View {
var user: User
var body: some View {
Text("signed-in username: \(user.userName)")
}
}
In Golang ozzo-validation, how can I validate a field which is dependent on another field ?
For example, if I have the following:
return validation.ValidateStruct(&c,
validation.Field(&c.Name, validation.Required, validation.Length(5, 20)),
validation.Field(&c.Gender, validation.In("Female", "Male")),
validation.Field(&c.Email, is.Email),
validation.Field(&c.Address),
How can I add a validation that the Address is required only if email is not empty?
You can achieve it in two ways-
Adding your own custom rules
Conditionally add FieldRules based on precondition-value i.e check Email while creating field rules then supply it to validation.ValidateStruct
For e.g.:
type Sample struct {
Name string
Gender string
Email string
Address Address
}
type Address struct {
// ... fields
}
func (s Sample) Validate() error {
var fieldRules []*validation.FieldRules
fieldRules = append(fieldRules, validation.Field(&s.Name, validation.Required, validation.Length(5, 20)))
fieldRules = append(fieldRules, validation.Field(&s.Gender, validation.In("Female", "Male")))
fieldRules = append(fieldRules, validation.Field(&s.Email, is.Email))
if len(strings.TrimSpace(s.Email)) > 0 {
fieldRules = append(fieldRules, validation.Field(&s.Address, validation.Required))
fieldRules = append(fieldRules, validation.Field(&s.Address))
}
return validation.ValidateStruct(&s, fieldRules...)
}
The library now supports conditional validation by the validation.When function.
Here is a code snipped which fits the validation you described.
package main
import (
"fmt"
validation "github.com/go-ozzo/ozzo-validation" // or "github.com/go-ozzo/ozzo-validation/v4" if "When" not found
)
type Entry struct {
Name string
Gender string
Email string
Address string
}
func main() {
v := func(e Entry) {
fmt.Println(validation.ValidateStruct(&e,
validation.Field(&e.Name, validation.Required, validation.Length(5, 20)),
// Note that if gender is "" and not required, validation returns no error.
validation.Field(&e.Gender, validation.Required, validation.In("Female", "Male")),
validation.Field(&e.Address, validation.When(e.Email != "", validation.Required.Error("Address is required if Email is set"))),
))
}
// All is fine for no Email.
e := Entry{
Name: "My name is!",
Gender: "Male",
}
v(e)
// Validation fails for Email and no Address.
e = Entry{
Name: "My name is!",
Gender: "Male",
Email: "a#org.com",
}
v(e)
}
It outputs.
<nil>
Address: Address is required if Email is set.
The library documentation describes it as well: https://github.com/go-ozzo/ozzo-validation#conditional-validation
I'm trying to do validation on my form struct in a method that returns a bool, but I keep getting false even when it should be returning true..
If you look towards the end of the Validate method, you'll see I write validated := len(this.Errors) == 0 which should be making "validated" either true or false based on whether the Errors map has items or not, and then I return validated.
When I fill out my form accurately, there should be no errors yet I still get false when I should be getting true.
Can someone explain? Is this not how Go works?
form.go:
package models
import (
"../config"
"../util"
)
type Form struct {
Name string
Email string
Phone string
Message string
Thanks string
ErrorHandler
}
func (this *Form) Validate() bool {
this.Errors = make(map[string]string)
matched := util.MatchRegexp(".+#.+\\..+", this.Email)
if !util.IsEmpty(this.Email) {
if matched == false {
this.Errors["Email"] = config.EMAIL_INVALID
}
} else {
this.Errors["Email"] = config.EMAIL_EMPTY
}
if util.IsEmpty(this.Name) {
this.Errors["Name"] = config.NAME_EMPTY
}
if util.IsEmpty(this.Phone) {
this.Errors["Phone"] = config.PHONE_EMPTY
}
if util.IsEmpty(this.Message) {
this.Errors["Message"] = config.MESSAGE_EMPTY
}
validated := len(this.Errors) == 0
if validated {
this.Thanks = config.THANK_YOU
}
return validated
}
errorhandler.go:
package models
type ErrorHandler struct {
Errors map[string]string
}
func (this *ErrorHandler) HandleErr(err string) {
this.Errors = make(map[string]string)
this.Errors["Error"] = err
}
And this is where I try to call the Validate method -- in a function in my controller:
form := &models.Form{
Name: r.FormValue("name"),
Email: r.FormValue("email"),
Phone: r.FormValue("phone"),
Message: r.FormValue("message")}
if form.Validate() {
// This never runs because 'form.Validate()' is always false
}
I don't think the util.IsEmpty() is the culprit here.. just checks if the string is empty:
func IsEmpty(str string) bool {
return strings.TrimSpace(str) == ""
}
Any help would be appreciated!
It's best to debug this kind of problem with a log statement like:
log.Printf("form: %v", form)
before calling validate, so it's clear what the input data looks like.
Greetings, Philip
Is there a good way to embed initialized struct variables in another struct?
Consider the following situation:
type Account struct {
AdminUser, AdminPass string
}
const (
acc1: "account_user",
pass: "111222"
)
var AccountDef = Account {
AdminUser: "acc1",
AdminPass: "pass1"
}
type Login struct {
Acc *AccountDef
Username, Password, Token string
}
var LoginDef = Login {
Token: "adaasdasddas1123"
}
I want to reuse AccountDef in Login, then I want to instantiate LoginDef in another function then use it for rendering templates like LoginDef.Acc.AdminUser
Is this possible to do?
If you want a Login to contain the fields from an Account, you can embed them like so:
http://play.golang.org/p/4DXnIsILd6
type Account struct {
AdminUser string
AdminPass string
}
type Login struct {
*Account
Username, Password, Token string
}
func main() {
acct := &Account{
AdminUser: "username",
AdminPass: "pass",
}
login := Login{Account: acct}
fmt.Println("login.AdminUser:", login.AdminUser)
fmt.Println("login.AdminPass:", login.AdminPass)
}