Django backend configuration to Stripe, React frontend - django

I have created React frontend for Stripe payment, how can I configure Django Rest Framework server ?
REACT COMPONENT
import React, { Fragment } from "react";
import StripeCheckout from "react-stripe-checkout";
import axios from "axios";
const StripeBtn = () => {
const publishableKey = "pk_test_some key";
const onToken = token => {
const body = {
amount: 999,
token: token
};
axios
.post("http://localhost:8000/payment", body)
.then(response => {
console.log(response);
alert("Payment Success");
})
.catch(error => {
console.log("Payment Error: ", error);
alert("Payment Error");
});
};
return (
<StripeCheckout
label="Go Premium" //Component button text
name="Business LLC" //Modal Header
description="Upgrade to a premium account today."
panelLabel="Go Premium" //Submit button in modal
amount={999} //Amount in cents $9.99
token={onToken}
stripeKey={publishableKey}
image="" //Pop-in header image
billingAddress={false}
/>
);
};
export default StripeBtn;
Regular Django views, instead of this I need Django Rest framework code to receive the axios POST request.
def charge(request): # new
if request.method == 'POST':
charge = stripe.Charge.create(
amount=500,
currency='usd',
description='A Django charge',
source=request.POST['stripeToken']
)
return render(request, 'payments/charge.html')
I have installed Stripe in Django and added key's in settings.py and above view is regular Django function to receive Stripe payments.
I was following a tutorial but they were using express.js as backend
https://medium.com/hackernoon/stripe-api-reactjs-and-express-bc446bf08301

Related

Next.js not receiving cookie from Django

I am messing around with Next.js and regular Django to do some testing, study etc.
I am trying to make a basic authentication system that simply takes the email and password, send to the Django server and it simply returns a cookie with the user id.
When I login with valid credentials, the server returns an "OK" (as it is supposed to do), however I don't get any of the cookies I'm supposed to have (not even the default csrf token from Django).
(sample data for design testing)
Using Django 4.0.6 and Next.js 12.3.1
Login route on Django
#csrf_exempt
def login(req):
body = json.loads(req.body.decode("utf-8"))
res = None
if (User.objects.filter(email=body["email"])):
user = User.objects.get(email=body["email"])
if (user.password == body["password"]):
res = JsonResponse({"success": True})
res.status_code = 200
res.set_cookie("AuthToken", json.dumps(
{"id": user.id}),
max_age=60 * 60 * 24,
httponly=False,
samesite="strict",
)
else:
res = JsonResponse({"success": False})
res.status_code = 401
else:
res = JsonResponse({"success": False})
res.status_code = 404
res.__setitem__("Access-Control-Allow-Origin", "*")
print(res.cookies)
return res
My login page on Next
import Link from "next/link";
import { useRouter } from "next/router";
import React, { FormEvent, useState } from "react";
import styled from "styled-components";
import { CONFIG } from "../../../DESIGN_CONFIG";
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const router = useRouter();
async function logIn(e: FormEvent) {
e.preventDefault();
const body = JSON.stringify({ email: email, password: password });
console.log(body);
console.log(process.env.API_URL);
const res = await fetch(process.env.API_URL + "login/", {
method: "POST",
body: body,
});
console.log(res);
if (res.status === 200) {
router.push("/");
}
}
return (
<Background>
<LoginForm onSubmit={logIn}>
<h1>LOGIN</h1>
<label htmlFor="email">E-Mail</label>
<input
type="email"
name="email"
id="email"
onChange={(e) => setEmail(e.target.value)}
required
/>
<label htmlFor="password">Password</label>
<input
type="password"
name="password"
id="password"
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit">Log In</button>
<span>
Or{" "}
<Link href="/user/sign-up">
<a>sign-up</a>
</Link>
</span>
</LoginForm>
</Background>
);
}
// This way down just got the styled-components for background and login form
Prints in the console of the body and the res (and the api route):
I'm using no middleware on Next nor Django and haven't changed anything in the _app.js.
I tried using invalid credentials and got the expected behavior.
I tried using Postman and it gets the cookies.
I tried making a simpler cookie.
res.set_cookie("AuthToken", user.id)

Webhook Payload Result is null in Dialogflow but not in Postman or Locally

I am failing to send over my personal banking data via a flask webhook from the Nordigen API to Dialogflow via fulfilment as only null is being received within the Dialogflow payload:
{
"fulfillmentText": "Your text response",
"fulfillmentMessages": [
{
"payload": [
null
]
}
]
}
The webhook error message is: Webhook call failed. Error: Failed to parse webhook JSON response: Expect a map object but found: [null].
When I just send the data as a fulfillmentText I receive "fulfillmentText": null.
I have tested my webhook with postman and there - as well as locally and other webhook'esque tests - everything is fine as I receive my most recent banking data.
The overall flow is twofold and simple:
User gets the correct banking and user specific login link to a specified bank, copy & pastes it to perform banking login by query_text = 'login'.
After a successful banking login the user can fetch different kinds of banking data (like balance) by query_text = 'balance'.
I went crazy with overengineering the flask webhook as I tried out many different things like asynchronous functions, uploading my Flask app to Heroku or CORS. I have even implemented an OAuth2 process where the user would query_text = 'google auth' and initiate the OAuth2 process in a step 0) by creating OAuth2 credentials and the Flask-Dance Python package. (Even though I have hardcoded the OAuth2 redirect link but this shouldn't be an issue atm). I was even trying to trick Dialogflow by creating a small Sqlite3 db within my webhook to at least upload the data there and then use it but without success.
So my question is .. what am I missing here? Why do I receive my banking data everywhere else but not in Dialogflow. My intuition is telling me Google is blocking this data for whatever reason.
Honestly I just don't know how to continue and I would appreciate any helpful comments!
This is my Flask webhook:
from dialogflow_fulfillment import QuickReplies, WebhookClient, Payload
from flask import Flask, request, jsonify, make_response, session, render_template, redirect, url_for
from flask_cors import CORS, cross_origin
import json
from json import JSONEncoder
import os
import asyncio
import requests
import sqlite3
from app.src.handler_login import handler_login
from app.src.handler_balance import handler_balance
from app.banking_data.init_db import create_connection
from flask_dance.contrib.google import make_google_blueprint, google
from oauthlib.oauth2.rfc6749.errors import InvalidGrantError, TokenExpiredError, OAuth2Error
from google.cloud import dialogflow_v2beta1 as dialogflow
from google.oauth2 import service_account
from uuid import uuid4
from nordigen import NordigenClient
# NORDIGEN
# Credentials
secret_id="XXX"
secret_key="XXX"
# Configuration
institution_id = "XXX"
app = Flask(__name__)
# set Flask secret key
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "supersekrit")
# GOOGLE API & AUTHENTICATION
app.config["GOOGLE_OAUTH_CLIENT_ID"] = "XXX"
app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = "XXX"
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = "1"
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = "1"
google_bp = make_google_blueprint(scope=["profile", "email"])
app.register_blueprint(google_bp, url_prefix="/login")
app.config['CORS_HEADERS'] = 'Content-Type'
cors = CORS(app, supports_credentials=True, resources={r"/webhook": {"origins": "*"}})
client = NordigenClient(
secret_id=secret_id,
secret_key=secret_key
)
client.generate_token()
# subclass JSONEncoder
class setEncoder(JSONEncoder):
def default(self, obj):
return list(obj)
#app.route("/")
def index():
if not google.authorized:
return redirect(url_for("google.login"))
try:
resp = google.get("/oauth2/v1/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
except (InvalidGrantError, TokenExpiredError) as e: # or maybe any OAuth2Error
return redirect(url_for("google.login"))
#app.route('/webhook', methods=['GET', 'POST', 'OPTION'])
async def webhook():
"""Handles webhook requests from Dialogflow."""
req = request.get_json(force=True)
query_text = req.get('queryResult').get('queryText')
if query_text:
if query_text == 'google auth':
if not google.authorized:
auth_link = 'MY HARD CODED GOOGLE AUTHENTICATION LINK HERE'
auth_link = {
"fulfillmentText": auth_link,
"source": 'webhook'
}
return auth_link
try:
resp = google.get("/oauth2/v1/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
except (InvalidGrantError, TokenExpiredError) as e: # or maybe any OAuth2Error
auth_link = 'MY HARD CODED GOOGLE AUTHENTICATION LINK HERE'
auth_link = {
"fulfillmentText": auth_link,
"source": 'webhook'
}
return auth_link
if query_text == 'login':
link = await handler_login(client, institution_id, session)
link = {
"fulfillmentText": link,
"source": 'webhook'
}
link = make_response(jsonify(link))
link.headers.add('Access-Control-Allow-Origin', '*')
return link
if query_text == 'balance':
balance = await handler_balance(client, session)
balance = {
"fulfillmentText": "Your text response",
"fulfillmentMessages": [
{
"text": {
"text": [
"Your text response"
]
}
},
{
"payload": {
balance
}
}
]
}
balance = json.dumps(balance, indent=4, cls=setEncoder)
balance = make_response(balance)
return balance
if __name__ == "__main__":
app.run(debug=True)
Here are two helper functions I have created that perform the creation of the login link the the fetching of my banking data via Nordigen:
from uuid import uuid4
async def handler_login(client, institution_id, session):
"""Handles the webhook request."""
# Initialize bank session
init = client.initialize_session(
# institution id
institution_id=institution_id,
# redirect url after successful authentication
redirect_uri="https://nordigen.com",
# additional layer of unique ID defined by you
reference_id=str(uuid4())
)
link = init.link
session["req_id"] = init.requisition_id
return link
async def handler_balance(client, session):
if "req_id" in session:
# Get account id after you have completed authorization with a bank
# requisition_id can be gathered from initialize_session response
#requisition_id = init.requisition_id
accounts = client.requisition.get_requisition_by_id(
requisition_id=session["req_id"]
)
# Get account id from the list.
account_id = accounts["accounts"][0]
#account_id = accounts["id"]
# Create account instance and provide your account id from previous step
account = client.account_api(id=account_id)
# Fetch account metadata
#meta_data = account.get_metadata()
# Fetch details
#details = account.get_details()
# Fetch balances
balance = account.get_balances()
balance = balance["balances"][0]
balance = balance["balanceAmount"]["amount"]
#balance = json.loads(balance)
# Fetch transactions
#transactions = account.get_transactions()
#agent.add(Payload({'balance': balance}))
return balance
Feel free to comment if you need any more input!

Difference between fetch and postman's results

I have a simple login backend which has two routes login and get_user. After a user is logged in, a cookie is set such that it enables other routes like get_user. I tested this backend with Postman and after correct login, the cookie is set and get_user responds with user's data.
However, when I try to use fetch or axios in React and JS, I get problems. After I fetch login, I can see the cookie is sent, however, fetch get_user acts like cookie is not set at all.
I provided a minimal example to show that server side session somehow doesn't work with fetch:
Frontend:
<!DOCTYPE html>
<html>
<body>
<h1> Set value: </h1> <h2 id="set_value"></h2>
<h1> Get value: </h1> <h2 id="get_value"></h2>
</body>
<script>
// ..\..\Flask_general_framework\backend\venv\Scripts\Activate.ps1
async function Set_user_fetch()
{
// Get user
let user = await fetch('http://127.0.0.1:5000/set/gggg', {
'method': 'GET',
//credentials: 'include',
mode: 'cors',
credentials: "same-origin",
headers: {'Content-type': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS},
'Access-Control-Allow-Headers': 'Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name',
}
})
user = await user.json();
console.log("await user:", user);
document.getElementById("set_value").innerHTML = user.value;
}
async function Get_user_fetch()
{
let user = await fetch('http://127.0.0.1:5000/get', {
'method': 'GET',
//credentials: 'include',
credentials: "same-origin",
mode: 'cors',
headers: {'Content-type': 'application/json', 'Accept': 'application/json',
'Access-Control-Allow-Origin': '*', // Required for CORS support to work
'Access-Control-Allow-Credentials': true, // Required for cookies, authorization headers with HTTPS},
'Access-Control-Allow-Headers': 'Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name',
}
})
user = await user.json();
console.log("await user:", user);
document.getElementById("get_value").innerHTML = user.value;
}
Set_user_fetch().then( () => {
Get_user_fetch();
});
</script>
</html>
Backend:
from re import I
from flask import Flask, session
from flask_session import Session
from flask_cors import CORS
import redis
import datetime as dt
app = Flask(__name__)
CORS(app, supports_credentials=True)
app.config['SECRET_KEY'] = 'super secret key'
#app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_PERMANENT'] = True
#app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:9876')
app.config['PERMANENT_SESSION_LIFETIME'] = dt.timedelta(days=7).total_seconds()
server_session = Session()
server_session.init_app(app)
#app.route('/set/<value>', methods=['GET', 'POST'])
def set_value(value):
session['value'] = value
return {"value": value}
#app.route('/get', methods=['GET', 'POST'])
def get_value():
return {"value": session.get('value', 'None')}
app.run(host='127.0.0.1', port=5000, debug=True)
Server Side
In order to support cross-site cookies in modern browsers, you need to configure your server to use the Set-Cookie attribute SameSite=None (see Flask-specific example here). Unfortunately, this also requires the Secure attribute and an HTTPS enabled server.
For local development, you can get around this by serving your client and server on the same hostname, eg localhost with SameSite=Lax (or omitting SameSite which defaults to "Lax").
By "same hostname" I mean that if your frontend code makes requests to localhost:5000, you should open it in your browser at http://localhost:<frontend-port>. Similarly, if you make requests to 127.0.0.1:5000, you should open it at http://127.0.0.1:<frontend-port>.
Lax same-site restrictions don't come into play if only the ports differ.
Client Side
You have a few problems here...
You're sending headers in your request that do not belong there. Access-Control-Allow-* are response headers that must come from the server.
You set credentials to same-origin but are sending a request to a different host. To use cookies, you need to set credentials to "include". See Request.credentials.
You have no error handling for non-successful requests.
You're also setting a lot of redundant properties and headers and can trim down your code significantly.
async function Set_user_fetch() {
const res = await fetch("http://127.0.0.1:5000/set/gggg", {
credentials: "include",
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
const user = await res.json();
console.log("await user:", user);
document.getElementById("set_value").innerHTML = user.value;
}
async function Get_user_fetch() {
const res = await fetch("http://127.0.0.1:5000/get", {
credentials: "include",
});
if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}
const user = await res.json();
console.log("await user:", user);
document.getElementById("get_value").innerHTML = user.value;
}
If you were using Axios, you would set the withCredentials config to true
axios.get("http://127.0.0.1:5000/set/gggg", {
withCredentials: true
});

Apollo client subscription pass JWT token handled by Django Channels middleware

I use Graphql subscriptions with Apollo client on a Vue3 app using Django graphQL Channels and DjangoGraphqlJWT packages in my backend app.
I'm trying to pass a JWT token on the Apollo subscriptions via the connectionParams.
Following this solution. I implemented a Middleware. However Apollo is passing the connectionParams as a payload. I can't find a way to access the payload at the Middleware level, but only on the consumer.
I could access the query string property from the scope argument in the middleware. However, I can't find a way to pass a query argument after the subscription is initiated.
CLIENT SIDE:
import { setContext } from "apollo-link-context";
import { Storage } from "#capacitor/storage";
import {
ApolloClient,
createHttpLink,
InMemoryCache,
split,
} from "#apollo/client/core";
import { getMainDefinition } from "#apollo/client/utilities";
import { WebSocketLink } from "#apollo/client/link/ws";
const authLink = setContext(async (_: any, { headers }: any) => {
const { value: authStr } = await Storage.get({ key: "auth" });
let token;
if (authStr) {
const auth = JSON.parse(authStr);
token = auth.token;
}
// return the headers to the context so HTTP link can read them
return {
headers: {
...headers,
authorization: token ? `JWT ${token}` : null,
},
};
});
const httpLink = createHttpLink({
uri: process.env.VUE_APP_GRAPHQL_URL || "http://0.0.0.0:8000/graphql",
});
const wsLink = new WebSocketLink({
uri: process.env.VUE_APP_WS_GRAPHQL_URL || "ws://0.0.0.0:8000/ws/graphql/",
options: {
reconnect: true,
connectionParams: async () => {
const { value: authStr } = await Storage.get({ key: "auth" });
let token;
if (authStr) {
const auth = JSON.parse(authStr);
token = auth.token;
console.log(token); // So far so good the token is logged.
return {
token: token,
};
}
return {};
},
},
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
const cache = new InMemoryCache();
export default new ApolloClient({
// #ts-ignore
link: authLink.concat(link),
cache,
});
BACKEND:
asgy.py
from tinga.routing import MyGraphqlWsConsumer
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from tinga.channels_middleware import JwtAuthMiddlewareStack
import os
from django.core.asgi import get_asgi_application
from django.conf.urls import url
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'tinga.settings')
application = get_asgi_application()
# import websockets.routing
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": JwtAuthMiddlewareStack(
URLRouter([
url(r"^ws/graphql/$", MyGraphqlWsConsumer.as_asgi()),
])
),
})
channels_middleware.py
#database_sync_to_async
def get_user(email):
try:
user = User.objects.get(email=email)
return user
except User.DoesNotExist:
return AnonymousUser()
class JwtAuthMiddleware(BaseMiddleware):
def __init__(self, inner):
self.inner = inner
async def __call__(self, scope, receive, send):
# Close old database connections to prevent usage of timed out connections
close_old_connections()
# Either find a way to get the payload from Apollo in order to get the token.
# OR
# Pass pass the token in query string in apollo when subscription is initiated.
# print(scope) # query_string, headers, etc.
# Get the token
# decoded_data = jwt_decode(payload['token'])
# scope["user"] = await get_user(email=decoded_data['email'])
return await super().__call__(scope, receive, send)
def JwtAuthMiddlewareStack(inner):
return JwtAuthMiddleware(AuthMiddlewareStack(inner))
As far as I understand, I can only access query string / URL params in the Middleware and not the Apollo payload. Would it be possible to pass the token for now in the query string? However since the token might not exist when Apollo client is provided, it needs to be reevaluated like the connectionParams.
Any workaround?
I managed to get the token in the consumer payload and inject the user into the context.
from tinga.schema import schema
import channels_graphql_ws
from channels.db import database_sync_to_async
from django.contrib.auth.models import AnonymousUser
from graphql_jwt.utils import jwt_decode
from core.models import User
from channels_graphql_ws.scope_as_context import ScopeAsContext
#database_sync_to_async
def get_user(email):
try:
user = User.objects.get(email=email)
return user
except User.DoesNotExist:
return AnonymousUser()
class MyGraphqlWsConsumer(channels_graphql_ws.GraphqlWsConsumer):
"""Channels WebSocket consumer which provides GraphQL API."""
schema = schema
# Uncomment to send keepalive message every 42 seconds.
# send_keepalive_every = 42
# Uncomment to process requests sequentially (useful for tests).
# strict_ordering = True
async def on_connect(self, payload):
"""New client connection handler."""
# You can `raise` from here to reject the connection.
print("New client connected!")
# Create object-like context (like in `Query` or `Mutation`)
# from the dict-like one provided by the Channels.
context = ScopeAsContext(self.scope)
if 'token' in payload:
# Decode the token
decoded_data = jwt_decode(payload['token'])
# Inject the user
context.user = await get_user(email=decoded_data['email'])
else:
context.user = AnonymousUser
And then passing the token in the connectionParams
const wsLink = new WebSocketLink({
uri: process.env.VUE_APP_WS_GRAPHQL_URL || "ws://0.0.0.0:8000/ws/graphql/",
options: {
reconnect: true,
connectionParams: async () => {
const { value: authStr } = await Storage.get({ key: "auth" });
let token;
if (authStr) {
const auth = JSON.parse(authStr);
token = auth.token;
console.log(token); // So far so good the token is logged.
return {
token: token,
};
}
return {};
},
},
});

How to add cookies to vue-apollo request?

I use vue-cli-plugin-apollo and I want to send language chosen by user from frontend to backend via cookie.
As a vue-apollo.js I use the next template
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
// Install the vue plugin
Vue.use(VueApollo)
// Name of the localStorage item
const AUTH_TOKEN = 'apollo-token'
// Http endpoint
const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:4000/graphql'
// Files URL root
export const filesRoot = process.env.VUE_APP_FILES_ROOT || httpEndpoint.substr(0, httpEndpoint.indexOf('/graphql'))
Vue.prototype.$filesRoot = filesRoot
// Config
const defaultOptions = {
// You can use `https` for secure connection (recommended in production)
httpEndpoint,
// You can use `wss` for secure connection (recommended in production)
// Use `null` to disable subscriptions
wsEndpoint: process.env.VUE_APP_GRAPHQL_WS || 'ws://localhost:4000/graphql',
// LocalStorage token
tokenName: AUTH_TOKEN,
// Enable Automatic Query persisting with Apollo Engine
persisting: false,
// Use websockets for everything (no HTTP)
// You need to pass a `wsEndpoint` for this to work
websocketsOnly: false,
// Is being rendered on the server?
ssr: false,
// Override default apollo link
// note: don't override httpLink here, specify httpLink options in the
// httpLinkOptions property of defaultOptions.
// link: myLink
// Override default cache
// cache: myCache
// Override the way the Authorization header is set
// getAuth: (tokenName) => ...
// Additional ApolloClient options
// apollo: { ... }
// Client local data (see apollo-link-state)
// clientState: { resolvers: { ... }, defaults: { ... } }
}
// Call this in the Vue app file
export function createProvider (options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options,
})
apolloClient.wsClient = wsClient
// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
// fetchPolicy: 'cache-and-network',
},
},
errorHandler (error) {
// eslint-disable-next-line no-console
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
},
})
return apolloProvider
}
taken from here. All options are shown here.
I have seen in different github discussions that cookies must be placed inside headers, for example here. Then I found, that apollo-link-http has headers option, so at the end I tried different variations of ...:
httpLinkOptions: {
headers: {
// Tried something like:
cookie[s]: 'language=en; path=/;'
// and something like:
cookie[s]: {
language: 'en'
}
}
}
but no luck.
In case of cookieS I receive Error: Network error: Failed to fetch.
In case of cookie, request is sent without issues, but backend does not see language cookie.
I double-checked backend using Postman and in this case backend receives request with manually added language cookie.
Could anyone help me?
Found solution.
FRONT END SETTINGS
Create cookie:
export function languageCookieSet (lang) {
document.cookie = `language=${lang}; path=/;`
}
Add httpLinkOptions to defaultOptions of vue-apollo.js.
const defaultOptions = {
...
httpLinkOptions: {
credentials: 'include'
},
...
BACKEND SETTINGS
As a backend I use Django (currently v2.2.7).
For development we need to use django-cors-headers
My development.py now looks like:
from .production import *
CORS_ORIGIN_WHITELIST = (
'http://localhost:8080',
)
CORS_ALLOW_CREDENTIALS = True
INSTALLED_APPS += ['corsheaders']
MIDDLEWARE.insert(0, 'corsheaders.middleware.CorsMiddleware')
Add to production.py:
LANGUAGE_COOKIE_NAME = 'language'
The default value of LANGUAGE_COOKIE_NAME is django_language, so if it is suitable for you, change
document.cookie = `language=${lang}; path=/;`
to
document.cookie = `django_language=${lang}; path=/;`
Now in backend we can get frontend language:
import graphene
from django.contrib.auth import get_user_model
from django.utils.translation import gettext as _
from .views import user_activation__create_email_confirmation
User = get_user_model()
class UserRegister(graphene.Mutation):
"""
mutation {
userRegister(email: "test#domain.com", password: "TestPass") {
msg
}
}
"""
msg = graphene.String()
class Arguments:
email = graphene.String(required=True)
password = graphene.String(required=True)
def mutate(self, info, email, password):
request = info.context
# Here we get either language from our cookie or from
# header's "Accept-Language" added by Browser (taken
# from its settings)
lang = request.LANGUAGE_CODE
print('lang:', lang)
if User.objects.filter(email=email).exists():
# In our case Django translates this string based
# on the cookie's value (the same as "request.LANGUAGE_CODE")
# Details: https://docs.djangoproject.com/en/2.2/topics/i18n/translation/
msg = _('Email is already taken')
else:
msg = _('Activation link has been sent to your email.')
user = User(email=email)
user.set_password(password)
user.save()
user_activation__create_email_confirmation(info.context, user)
return UserRegister(msg=msg)
Note: I have not tested yet these changes in production, but in production I use just one server where frontend and backend live behind nGinx and this is the reason why CORS settings live in development.py instead of production.py. Also in production credentials: 'include' possibly could be changed to credentials: 'same-origin' (ie more strict).