Save value to Firestore on Stripe successful payment status - reactjs

I want to write the user free-input form data to Firestore based on them successfully completing the Stripe checkout through the PaymentElement. I was first trying to do so based on the stripe.confirmPayment method response, which did not work well. Any ideas or practical examples on how can this be implemented? I have also tried using Stripe Webhooks, but I am unsure how to pass the user free-input form data there. Can this generally be handled from the front end, or should I create Firebase function?
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
return_url: window.location.href
}
});

Related

Approving or disapproving requests in one flutter app from another flutter app

So, I have created two applications, user and admin, using flutter. And both of them are in a single firebase project.
The Applications are investment-related. What I want to do is, Suppose a user sends an investment request from the user app. Another user in the admin app should be able to approve or disapprove the request and then the request status should be visible in the user app. Is there any way to do it?
What I tried: I created a new field in Firebase Realtime Database named Request Status. Then I passed a String Approved into it through the Admin app but it didn't work.
...
final investmentRequestsRef = database.child("Investment Requests");
...
void addData(String requestStatus) {
investmentRequestRef.set({
"Request Status": requestStatus,
});
}
...
Code of button that updates the status:
ElevatedButton(
...
onPressed: () {
addData("Approved");
},
...
)
What happens in realtime database when I do the method
You should try something like this...
DatabaseReference ref = FirebaseDatabase.instance.ref("Investment Requests/${requestID}");
await ref.update({
"Request Status": requestStatus,
});
This should work... I haven't seen your full code so I can't add anything else without more details about your code.
Use this official link of FlutterFire for reference.

Is there a way to remove previous session without hard refreshing (sveltekit and Supabase)

im practicing authentication at the moment with sveltekit and Supabase
RLS is on and one of the issues im having is after i log out, and i sign in with another email, i can see the info from the previous session and when i hard refresh, then i see the correct data. Im not sure how to fix this
in my store
export const user = readable(null, (set) => {
set(supabase.auth.user());
const unsubscribe = supabase.auth.onAuthStateChange((_, session) => {
session ? set(session.user) : set(null);
});
return () => {
unsubscribe.data.unsubscribe();
};
});
I currently have a similar problem.
Where after sign out, still the old user persists somehow.
My auth is only on the clientside.
I use sveltekit (as a static site generator)
after signOut() there is still the sb-refresh-token cookie and the sb-access-token cookie with the jwt
thus, supabase.auth.getUser() still gets me the (old)user
so user does not get logged out.
what helped me is to reload the page after the signout()
aka not use the linking and goto() function of my framework but rather use the browser function
window.location.href = '/loggedOut';
This deleted the cookies for me

Django, Djoser social auth : State could not be found in server-side session data. status_code 400

I'm implementing an auth system with django and react. The two app run respectively on port 8000, 3000. I have implemented the authentication system using the Djoser package. This package uses some dependencies social_core and social_django. Everything seems to be configured ok. I click on login google button...I'm redirected to the google login page and then back to my front-end react app at port 3000 with the state and code parameters on the url.
At this point I'm posting those parameters to the backend. The backend trying to validate the state checking if the state key is present in the session storage using the code below from (social_core/backends/oauth.py)
def validate_state(self):
"""Validate state value. Raises exception on error, returns state
value if valid."""
if not self.STATE_PARAMETER and not self.REDIRECT_STATE:
return None
state = self.get_session_state()
request_state = self.get_request_state()
if not request_state:
raise AuthMissingParameter(self, 'state')
elif not state:
raise AuthStateMissing(self, 'state')
elif not constant_time_compare(request_state, state):
raise AuthStateForbidden(self)
else:
return state
At this point for some reasons the state session key is not there..and I receive an error saying that state cannot be found in session data ( error below )
{"error":["State could not be found in server-side session data."],"status_code":400}
I recap all the action I do:
Front-end request to backend to generate given the provider google-oauth2 a redirect url. With this action the url is generated also the state key is stored on session with a specific value ( google-oauth2_state ).
Front-end receive the url and redirect to google auth page.
Authentication with google and redirection back to the front-end with a state and code parameters on the url.
Front-end get the data form url and post data to back-end to verify that the state received is equal to the generated on the point (1).
For some reasons the state code is not persisted... Any ideas and help will be really appreciated.
Thanks to all.
ok so this is a common problem while you are working with social auth. I had the same problem for so many times.
The flow:
make a request to http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/ (example)
you will get a authorization_url. if you notice in this authorization_url there is a state presented . this is the 'state of server side'.
now you need to click the authorization_url link.Then you will get the google auth page.After that you will be redirect to your redirect url with a state and a code. Remember this state should be the same state as the server side state .(2)
make post req to http://127.0.0.1:8000/auth/o/google-oauth2/?state=''&code=''.
if your states are not the same then you will get some issue.
everytime you wanna login , you need to make a request to http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/
and then to http://127.0.0.1:8000/auth/o/google-oauth2/?state=''&code='' thus you will get the same state.
Without necessary detailed information, I can only tell 2 possible reasons:
You overrode backend with improper session operations(or the user was logged out before auth was finished).
Front-end used incorrect state parameter
You could test social login without front-end, let's say if you're trying to sign in with Google:
Enter the social login URL in browser, like domain.com:8000/login/google-oauth2/
Authorize
See if the page redirected to your default login page correctly
If yes, then probably you need to check your front-end code, and if no, then check your backend code.
At the end, if you're not so sensitive to the potential risk, you could also override GoogleOAuth2 class as following to disable state check:
from social_core.backends import google
class GoogleOAuth2(google.GoogleOAuth2):
STATE_PARAMETER = False
I think you may need some changes in you authorizing flow in step NO.3 and 4.
3.Authentication with google and redirection back to the front-end with a state and code parameters on the url.
4.Front-end get the data form url and post data to back-end to verify that the state received is equal to the generated on the point (1).
maybe you should redirect back to server side after google's authorization.
then at the server side, do the check! validate the state and code (maybe do more things).
then let server redirect to the front-end site you wanted to before.
for some reason, redirect to front-end directly will miss the param.. :-)
Finally, I reach a point where everything is working 200 percent fine, on local as well as production.
The issue was totally related to the cookies and sessions:
So rite answer typo is
make it look to your backend server as if the request is coming from localhost:8000, not localhost:3000,
means the backend domain should be the same always.
For making it possible you have two ways:
1: server should serve the build of the frontend then your frontend will always be on the same domain as the backend.
2: make a simple view in django and attach an empty template to it with only a script tag including logic to handle google auth. always when you click on signing with google move back you you're that view and handle the process and at the end when you get back your access token pass it to the frontend through params.
I used 2nd approach as this was appropriate for me.
what you need to do is just make a simple View and attach a template to it so on clicking on signIN with google that view get hit. and other process will be handled by the view and on your given URL access token will be moved.
View Code:
class GoogleCodeVerificationView(TemplateView):
permission_classes = []
template_name = 'social/google.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["redirect_uri"] = "{}://{}".format(
settings.SOCIAL_AUTH_PROTOCOL, settings.SOCIAL_AUTH_DOMAIN)
context['success_redirect_uri'] = "{}://{}".format(
settings.PASSWORD_RESET_PROTOCOL, settings.PASSWORD_RESET_DOMAIN)
return context
backend script code:
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script>
function redirectToClientSide(success_redirect_uri) {
window.location.replace(`${success_redirect_uri}/signin/`);
}
function getFormBoday(details) {
return Object.keys(details)
.map(
(key) =>
encodeURIComponent(key) + "=" + encodeURIComponent(details[key])
)
.join("&");
}
try {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
const redirect_uri = "{{redirect_uri|safe}}";
const success_redirect_uri = "{{success_redirect_uri|safe}}";
if (params.flag === "google") {
axios
.get(
`/api/accounts/auth/o/google-oauth2/?redirect_uri=${redirect_uri}/api/accounts/google`
)
.then((res) => {
window.location.replace(res.data.authorization_url);
})
.catch((errors) => {
redirectToClientSide(success_redirect_uri);
});
} else if (params.state && params.code && !params.flag) {
const details = {
state: params.state,
code: params.code,
};
const formBody = getFormBoday(details);
// axios.defaults.withCredentials = true;
axios
.post(`/api/accounts/auth/o/google-oauth2/?${formBody}`)
.then((res) => {
const formBody = getFormBoday(res.data);
window.location.replace(
`${success_redirect_uri}/google/?${formBody}`
);
})
.catch((errors) => {
redirectToClientSide(success_redirect_uri);
});
} else {
redirectToClientSide(success_redirect_uri);
}
} catch {
redirectToClientSide(success_redirect_uri);
}
</script>
</body>

Firebase - Best Practice For Server Firestore Reads For Server-Side Rendering

I have a server-side-rendered reactjs app using firebase firestore.
I have an area of my site that server-side-renders content that needs to be retrieved from firestore.
Currently, I am using firestore rules to allow anyone to read data from these particular docs
What worries me is that some bad person could setup a script to just continuously hit my database with reads and rack up my bills (since we are charged on a per-read basis, it seems that it's never wise to allow anyone to perform reads.)
Current Rule
// Allow anonymous users to read feeds
match /landingPageFeeds/{pageId}/feeds/newsFeed {
allow read: if true;
}
Best Way Forward?
How do I allow my server-side script to read from firestore, but not allow anyone else to do so?
Keep in mind, this is an initial action that runs server-side before hydrating the client-side with the pre-loaded state. This function / action is also shared with client-side for page-to-page navigation.
I considered anonymous login - which worked, however, this generated a new anonymous user with every page load - and Firebase does throttle new email/password and anonymous user accounts. It did not seem practical.
Solution
Per Doug's comment, I thought about the admin SDK more. I ended up creating a separate API in firebase functions for anonymous requests requiring secure firestore reads that can be cached.
Goals
Continue to deny public reads of my firestore database
Allow anonymous users to trigger firestore reads for server-side-rendered reactjs pages that require data from Firestore database (like first-time visitors, search engines).
Prevent "read spam" where a third party could hit my database with millions of reads to drive up my cloud costs by using server-side CDN cache for the responses. (by invoking unnessary reads in a loop, I once racked up a huge bill on accident - I want to make sure strangers can't do this maliciously)
Admin SDK & Firebase Function Caching
The admin SDK allows me to securely read from firestore. My firestore security rules can deny access to non-authenticated users.
Firebase functions that are handling GET requests support server caching the response. This means that subsequent hits from identical queries will not re-run all of my functions (firebase reads, other function invocations) - it will just instantly respond with the same data again.
Process
Anonymous client visits a server-side rendered reactjs page
Initial load rendering on server triggers a firebase function (https trigger)
Firebase function uses Admin SDK to read from secured firestore database
Function caches the response for 3 hours res.set('Cache-Control', 'public, max-age=600, s-maxage=10800');
Subsequent requests from any client anywhere for the next 3 hours are served from the cache - avoiding unnecessary reads or additional computation / resource usage
Note - caching does not work on local - must deploy to firebase to test caching effect.
Example Function
const functions = require("firebase-functions");
const cors = require('cors')({origin: true});
const { sendResponse } = require("./includes/sendResponse");
const { getFirestoreDataWithAdminSDK } = require("./includes/getFirestoreDataWithAdminSDK");
const cachedApi = functions.https.onRequest((req, res) => {
cors(req, res, async () => {
// Set a cache for the response to limit the impact of identical request on expensive resources
res.set('Cache-Control', 'public, max-age=600, s-maxage=10800');
// If POST - response with bad request code - POST requests are not cached
if(req.method === "POST") {
return sendResponse(res, 400);
} else {
// Get GET request action from query
let action = (req.query.action) ? req.query.action : null;
console.log("Action: ", action);
try {
// Handle Actions Appropriately
switch(true) {
// Get Feed Data
case(action === "feed"): {
console.log("Getting feed...");
// Get feed id
let feedId = (req.query.feedId) ? req.query.feedId : null;
// Get feed data
let feedData = await getFirestoreDataWithAdminSDK(feedId);
return sendResponse(res, 200, feedData);
}
// No valid action specified
default: {
return sendResponse(res, 400);
}
}
} catch(err) {
console.log("Cached API Error: ", err);
return sendResponse(res, 500);
}
}
});
});
module.exports = {
cachedApi
}

Adding an object to skygear Social Login API

I am using skygear social login. i want to append an object along with the request to the socialLogin api.The values in the object are a field in my UI application which needs to be saved in my db. The googleoauth 2.0 uses state to pass additional data in the query params . However I am not able to do so with skygear.
this is my code
function oauthlogin(provider,Cname,Cage){
return (
skygear.auth.loginOAuthProviderWithPopup('google',options:
{Cname,cage}).then(res=>{console.log(res})
)
}
I have tried options and state as it was given in the official documentation of Skygear.
Thanks in advance.
The skygear.auth.loginOAuthProviderWithPopup does accept options, but they are for auth only, which will not be saved as a field in the user profile.
Suggested way: save the fields via save record API after the login is successful (in the then part).
function oauthlogin(provider,Cname,Cage){
return (
skygear.auth.loginOAuthProviderWithPopup('google')
.then((user) => {
// save the user data here instead
user.Cname = Cname;
user.Cage = Cage;
skygear.publicDB.save(user).then(res => console.log(res));
})
)
}
Hope it helps!

Resources