Flutter WooCommerce Customer, not registering using Dio? - wordpress

I am buidling flutter woocommerce store app for customers. I want to create/register a new customer using Dio() and I am using WordPress Plugin JWT Authentication for WP REST API for authentication. Here if i want to access URL for Customers Its showing following response. URL= "https://example.com/wp-json/wc/v3/customers"
My Custutomer Model file is:
class CustomerModel {
String email;
String firstName;
String lastName;
String password;
CustomerModel(
{required this.email,
required this.firstName,
required this.lastName,
required this.password});
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {};
map.addAll({
'email': email,
'first_name': firstName,
'last_name': lastName,
'password': password,
'username': email
});
return map;
}
}
My API Service Mothod to create customer is:
Future<bool> createCustomer(CustomerModel model) async {
var authToken =
base64.encode(utf8.encode(Config.key + ':' + Config.sceret));
print(authToken);
bool ret = false;
try {
print("${Config.url+ Config.customerURL}");
var response = await Dio().post(Config.url + Config.customerURL,
data: model.toJson(),
options: new Options(headers: {
HttpHeaders.authorizationHeader: 'Basic $authToken',
HttpHeaders.contentTypeHeader: 'application/json'
}));
print(response.statusCode);
if (response.statusCode == 201) {
ret = true;
}
} on DioError catch (e) {
if (e.response?.statusCode == 404) {
ret = false;
} else {
ret = false;
}
}
return ret;
}
I am sharing my source code. Please download and solve.
https://drive.google.com/file/d/1ZFjsjeEqsGHVJXbmye_BB98gcD-bQFAW/view?usp=share_link

HttpHeaders.authorizationHeader: 'Basic $authToken'
Make this 'Bearer $authToken'

Related

Flutter WooCommerce Customer registeration failed using Dio and jwt?

I am buidling flutter woocommerce store app for customers. I want to create/register a new customer using Dio() and I am using WordPress Plugin JWT Authentication for WP REST API for authentication. Here if i want to access URL for Customers Its showing following response. URL= "https://example.com/wp-json/wc/v3/customers"
{"code":"rest_no_route","message":"No route was found matching the URL and request method.","data":{"status":404}}
CUSTOMER MODEL FILE IS...
class CustomerModel {
String email;
String firstName;
String lastName;
String password;
CustomerModel(
{required this.email,
required this.firstName,
required this.lastName,
required this.password});
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {};
map.addAll({
'email': email,
'first_name': firstName,
'last_name': lastName,
'password': password,
'username': email
});
return map;
}
}
API MODEL FILE IS...
Future<bool> createCustomer(CustomerModel model) async {
var authToken =
base64.encode(utf8.encode(Config.key + ':' + Config.sceret));
print(authToken);
bool ret = false;
try {
print("${Config.url+ Config.customerURL}");
var response = await Dio().post(Config.url + Config.customerURL,
data: model.toJson(),
options: new Options(headers: {
HttpHeaders.authorizationHeader: 'Basic $authToken',
HttpHeaders.contentTypeHeader: 'application/json'
}));
print(response.statusCode);
if (response.statusCode == 201) {
ret = true;
}
} on DioError catch (e) {
if (e.response?.statusCode == 404) {
ret = false;
} else {
ret = false;
}
}
return ret;
}
404 means that url that you are requesting is not exist on the server.
Double-check the actual URL you are requesting and compare with expected one.
404 Not Found
The requested resource could not be found but may be available in the future. Subsequent requests by the client are permissible.

Flutter - Type 'Null' is not a sub type of string

I'm trying to use Firebase to login, but I'm getting Type 'Null' is not a sub type of string as error when I try to parse _userId = responseData['localId'];
When I try to parse _token = responseData['idToken'];, it works correctly.
responseData['localId'] is not NULL.
Here is full code:
class Auth with ChangeNotifier {
String? _token;
String? _userId;
Future<void> _authenticate(
String email, String password, String urlSegment) async {
var url = Uri.parse(
'https://www.google.com');
var body = json.encode({
'email': email,
'password': password,
'returnSecureToken': true,
});
try {
final response = await http.post(url, body: body);
final responseData = json.decode(response.body);
_token = responseData['idToken'];
_userId = responseData['localId'];
} catch (err) {
throw err;
}
}
Future<void> login(String email, String password) async {
return _authenticate(email, password, 'signInWithPassword');
}
}

How to match Firebase User UID with Firestore Doc ID - Flutter

I am new to flutter. So Please kindly bear with me.
I am creating a Real Estate App using Firebase and Provider.
I have two root Collections (1) "users" and (2) "properties"
I would like to achieve one specific task.
Although it seems like a simple task, yet I am still unable to solve it.
The task is to store Firebase User UID as Firestore Document ID of the rool Collection called "users" when the user sign up.
The problem I am having is the Firestore Document ID is automatically generated and its not Firebase User UID. Plus the field for userId appears null in Firestore.
Please see the screenshot of my database here
Thank you for your attention and time.
Here is my user_model.dart
class NayyaaUser {
String? uid;
String name;
String email;
String? phone;
NayyaaUser({this.uid, required this.name, required this.email, this.phone});
//send data to Firestore
Map<String, dynamic> toMap() {
return {
'userId': uid,
'userName': name,
'userEmail': email,
'userPhone': phone,
};
}
//draw data from firestore
factory NayyaaUser.fromFirestore(Map<String, dynamic> firestore) =>
NayyaaUser(
uid: firestore['userId'],
email: firestore['userEmail'] ?? " ",
name: firestore['userName'] ?? " ",
phone: firestore['userPhone'] ?? " ",
);
}
Here is my user_provider.dart
class UserProvider extends ChangeNotifier {
final firestoreService = FirestoreService();
final authService = AuthService();
String? _userId;
String? _name;
String? _email;
String? _phone;
//getters
String? get userId => _userId;
String? get name => _name;
String? get email => _email;
String? get phone => _phone;
//setters
changeName(String value) {
_name = value;
notifyListeners();
}
changeEmail(String value) {
_email = value;
notifyListeners();
}
changePhone(String value) {
_phone = value;
notifyListeners();
}
saveUserProfile() {
if (_userId == null) {
var updateUserProfile = NayyaaUser(
// userId: _userId,
uid: _userId,
name: name ?? '',
email: email ?? '',
phone: phone ?? '');
firestoreService.saveUserDataToFirestore(updateUserProfile);
} else {
var newUserProfile = NayyaaUser(
// userId: _userId,
uid: _userId,
name: name ?? '',
email: email ?? '',
phone: phone ?? '');
firestoreService.saveUserDataToFirestore(newUserProfile);
}
}
}
Here is auth_service.dart
class AuthService {
final FirebaseAuth _authInstance = FirebaseAuth.instance;
//create user obj based on "User" from Firebase
NayyaaUser? _userFromFirebase(User? user) {
return user != null
? NayyaaUser(
uid: user.uid,
name: '',
email: '',
phone: '',
)
: null;
}
// auth change user stream
Stream<NayyaaUser?> get userAuthStatus {
return _authInstance.authStateChanges().map(_userFromFirebase);
}
// sign in with email + password
Future signIn(String email, String password) async {
try {
UserCredential userAuthResult = await _authInstance
.signInWithEmailAndPassword(email: email, password: password);
User? user = userAuthResult.user;
return _userFromFirebase(user!);
} catch (e) {
// ignore: avoid_print
print(e.toString());
return null;
}
}
Future signUp(String email, String password) async {
try {
UserCredential userAuthResult = await _authInstance
.createUserWithEmailAndPassword(email: email, password: password);
User? user = userAuthResult.user;
return _userFromFirebase(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _authInstance.signOut();
} catch (e) {
// ignore: avoid_print
print(e.toString());
return null;
}
}
}
Here is firestore_service.dart
class FirestoreService {
final CollectionReference _userRef =
FirebaseFirestore.instance.collection('users');
final CollectionReference _propertyRef =
FirebaseFirestore.instance.collection('properties');
//add or update user to firestore
Future<void> saveUserDataToFirestore(NayyaaUser nayyaaUserData) {
return _userRef.doc(nayyaaUserData.uid).set(nayyaaUserData.toMap());
}
// fetch user data from firestore
Stream<List<NayyaaUser>> getNayyaaUser() {
return _userRef.snapshots().map((snapshot) => snapshot.docs
.map((document) =>
NayyaaUser.fromFirestore(document.data() as Map<String, dynamic>))
.toList());
}
}
After login/signup successfully you can get user id from FirebaseAuth.instance.currentUser?.uid

Flutter Firestore adding data

I want to add data on firestore and it wont work. can somebody help me.
This is the newest updated version and I can't figure out how...
firebase_auth: ^0.18.0+1 cloud_firestore: ^0.14.0+2
This is the sign up screen so I want to send data after I create the email and password.
I want to add the document with user uid too.
onPressed: () async {
try {
UserCredential userCredential = await FirebaseAuth
.instance
.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
if (userCredential != null) {
firestore
.collection("user")
.doc('user.uid')
.set({
'username': username,
'email': email,
})
.then((value) => print("User Added"))
.catchError((error) =>
print("Failed to add user: $error"));
Navigator.of(context).pushNamed(AppRoutes.authLogin);
}
} catch (e) {
print(e);
_usernameController.text = "";
_passwordController.text = "";
_repasswordController.text = "";
_emailController.text = "";
//TODO: alertdialog with error
}
setState(() {
saveAttempted = true;
});
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
}
},
Can someone help me with the firestore.. Thank you..
First Create a User class.
class UserData {
final String userId;
final String fullNames;
final String email;
final String phone;
UserData(
{this.userId,
this.fullNames,
this.email,
this.phone});
Map<String, dynamic> getDataMap() {
return {
"userId": userId,
"fullNames": fullNames,
"email": email,
"phone": phone,
};
}
}
Then you can use a function like this one to save the credentials and save the data to firestore
createOrUpdateUserData(Map<String, dynamic> userDataMap) async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
DocumentReference ref =
Firestore.instance.collection('user').document(user.uid);
return ref.setData(userDataMap, merge: true);
}
==
bool validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
void validateAndSubmit() async {
if (validateAndSave()) {
try {
String userId = _formType == FormType.login
? await widget.auth.signIn(_email, _password)//use your signin
: await widget.auth.signUp(_email, _password);//use your signup
if (_formType == FormType.register) {
UserData userData = new UserData(
fullNames: _fullNames,
email: _email,
phone: "",
);
createOrUpdateUserData(userData.getDataMap());
}
} catch (e) {
setState(() {
_isLoading = false;
switch (e.code) {
case "ERROR_INVALID_EMAIL":
_authHint = "Your email address appears to be malformed.";
break;
case "ERROR_EMAIL_ALREADY_IN_USE":
_authHint = "Email address already used in a different account.";
break;
case "ERROR_WRONG_PASSWORD":
_authHint = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
_authHint = "User with this email doesn't exist.";
break;
case "EMAIL NOT VERIFIED":
_authHint = "Email not verified: Please go to yor email and verify";
break;
case "ERROR_USER_DISABLED":
_authHint = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
_authHint =
"Too many Attemps. Account has temporarily disabled.\n Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
_authHint = "Signing in with Email and Password is not enabled.";
break;
case "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL":
_authHint = "The email is in use by another account";
break;
default:
_authHint = "An undefined Error happened.";
}
});
print(e);
errorDialog(context, _authHint);
}
} else {
setState(() {
_authHint = '';
});
}
}
Then use
onpressed:(){
validateAndSubmit();
}
the formtype is an Enum
enum FormType { login, register, reset }
widget.auth.signIn and widget.auth.signUp should be replaced with your signin and signup respectively.
Added a custom error block to differentiate firebase auth errors as well.
Defining an auth page independently will help you reuse your code in future.

flutter firebase auto refresh user session with refreshToken

I want user in my app to stay logged in. I'm using the firebase authentification with IDToken which lasts for 1hour until it expires. I want to auto refresh the session everytime if it is going to expire.
what Ive read so far here https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token it should be somehow possible with https://securetoken.googleapis.com/v1/token?key=[API_KEY]
This is my full code for authentification right now (flutter)
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../provider/http_exception.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
class Auth with ChangeNotifier {
String _token;
DateTime _expiryDate;
String _userId;
Timer _authTimer;
bool wasLoggedOut = false;
bool onBoarding = false;
Future<void> createUser(String email, String firstName, String lastName) async {
final url = 'https://test45.firebaseio.com/users/$userId.json?auth=$token';
final response = await http.put(url, body: json.encode({
'userEmail': email,
'userIsArtist': false,
'userFirstName': firstName,
'userLastName': lastName,
}));
print('post ist done');
print(json.decode(response.body));
}
bool get isAuth {
return token != null;
}
String get userId {
return _userId;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
Future<void> authenticate(
String email, String password, String urlSegement) async {
final url = 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegement?key=AIzaSyD8pb3M325252dfsDC-4535dfd';
try {
final response = await http.post(url,
body: json.encode({
'email': email,
'password': password,
'returnSecureToken': true,
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expiresIn'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return authenticate(email, password, 'signUp');
}
Future<void> signin(String email, String password) async {
return authenticate(email, password, 'signInWithPassword');
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if(!prefs.containsKey('userData')){
return false;
}
final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>;
final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
if(expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'];
_userId = extractedUserData['userId'];
_expiryDate = expiryDate;
notifyListeners();
_autoLogout();
return true;
}
Future<void> logout() async {
_token = null;
_userId = null;
_expiryDate = null;
if(_authTimer != null){
_authTimer.cancel();
_authTimer = null;
}
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.remove('userData');
}
void _autoLogout() {
if(_authTimer != null) {
_authTimer.cancel();
}
final timetoExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timetoExpiry), logout);
}
}
how to modify my auth.dart to achieve the auto refreshing?
EDIT:
As mentioned in the comments, im working with providers where I have the following functions to retrieve the token:
update(String token, id, List<items> itemsList) {
authToken = token;
userId = id;
}
also in every of my API calls im using the auth parameter already:
var url = 'https://test45.firebaseio.com/folder/$inside/$ym.json?auth=$authToken';
I just need somebody who can show me how to modify my code with the refresh token.
Thanks in advance!
EDIT:
I tried to implement it, but im getting an infinite loop, please help:
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
refreshSession();
}
Future<void> refreshSession() async {
final url = 'https://securetoken.googleapis.com/v1/token?key=5437fdjskfsdk38438?grant_type=refresh_token?auth=$token';
try {
final response = await http.post(url,
body: json.encode({
'token_type': 'Bearer',
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['id_token'];
_userId = responseData['user_id'];
_expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expires_in'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
I edited your refresh_token() function.
Firstly, you should use your web api key on your firebase project with the link. You should also save the refresh token. And if you post like this, it will work. If don't work, try without json.encode() function on your body as I commit.
Future<void> refreshSession() async {
final url =
'https://securetoken.googleapis.com/v1/token?key=$WEB_API_KEY';
//$WEB_API_KEY=> You should write your web api key on your firebase project.
try {
final response = await http.post(
url,
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded"
},
body: json.encode({
'grant_type': 'refresh_token',
'refresh_token': '[REFRESH_TOKEN]', // Your refresh token.
}),
// Or try without json.encode.
// Like this:
// body: {
// 'grant_type': 'refresh_token',
// 'refresh_token': '[REFRESH_TOKEN]',
// },
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['id_token'];
_refresh_token = responseData['refresh_token']; // Also save your refresh token
_userId = responseData['user_id'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expires_in'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refresh_token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
This is your full auth.dart file which I edited.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../provider/http_exception.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
class Auth with ChangeNotifier {
String _token;
String _refresh_token;
DateTime _expiryDate;
String _userId;
Timer _authTimer;
bool wasLoggedOut = false;
bool onBoarding = false;
Future<void> createUser(String email, String firstName, String lastName) async {
final url = 'https://test45.firebaseio.com/users/$userId.json?auth=$token';
final response = await http.put(url, body: json.encode({
'userEmail': email,
'userIsArtist': false,
'userFirstName': firstName,
'userLastName': lastName,
}));
print('post ist done');
print(json.decode(response.body));
}
bool get isAuth {
return token != null;
}
String get userId {
return _userId;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null && _refresh_token!=null) {
return _token;
}
refreshSession();
return null;
}
Future<void> authenticate(
String email, String password, String urlSegement) async {
final url = 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegement?key=AIzaSyD8pb3M325252dfsDC-4535dfd';
try {
final response = await http.post(url,
body: json.encode({
'email': email,
'password': password,
'returnSecureToken': true,
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
_refresh_token = responseData['refreshToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expiresIn'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refresh_token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return authenticate(email, password, 'signUp');
}
Future<void> signin(String email, String password) async {
return authenticate(email, password, 'signInWithPassword');
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if(!prefs.containsKey('userData')){
return false;
}
final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>;
final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
if(expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'];
_refresh_token = extractedUserData['refresh_token'];
_userId = extractedUserData['userId'];
_expiryDate = expiryDate;
notifyListeners();
_autoLogout();
return true;
}
Future<void> logout() async {
_token = null;
_refresh_token = null;
_userId = null;
_expiryDate = null;
if(_authTimer != null){
_authTimer.cancel();
_authTimer = null;
}
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.remove('userData');
}
void _autoLogout() {
if(_authTimer != null) {
_authTimer.cancel();
}
final timetoExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timetoExpiry), logout);
}
Future<void> refreshSession() async {
final url =
'https://securetoken.googleapis.com/v1/token?key=$WEB_API_KEY';
//$WEB_API_KEY=> You should write your web api key on your firebase project.
try {
final response = await http.post(
url,
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded"
},
body: json.encode({
'grant_type': 'refresh_token',
'refresh_token': '[REFRESH_TOKEN]', // Your refresh token.
}),
// Or try without json.encode.
// Like this:
// body: {
// 'grant_type': 'refresh_token',
// 'refresh_token': '[REFRESH_TOKEN]',
// },
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['id_token'];
_refresh_token = responseData['refresh_token']; // Also save your refresh token
_userId = responseData['user_id'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expires_in'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refresh_token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
}
You need to save the refresh token.
Follow this topic to refresh your IDToken using the refresh token: https://firebase.google.com/docs/reference/rest/auth#section-refresh-token
When making any calls to the API, use a function to retrieve the IDToken. This function must check if the current IDToken is still valid and, if not, ask for a new one (using the link provided).
I think the Dio library is right for you
dio = Dio();
dio.options.baseUrl = URL_API_PROD;
dio.interceptors.add(InterceptorsWrapper(
onRequest: (Options option) async{
//getToken() : you can check token expires and renew in this function
await getToken().then((result) {
token = result;
});
option.headers = {
"Authorization": "Bearer $token"
};
}
));
Response response = await dio.get('/api/users');
body expects string...Hence change body in refreshSession() to body: 'grant_type=refresh_token&refresh_token=[YOUR REFRESH TOKEN]',.
You need to load 'refreshToken' from SharedPreferences before sending http.post request.

Resources