Reddit's access token has an expiration of 1 hour, but I want users that log in to my app to be able to post comments on Reddit for example. This means I need to refresh their access token once it has expired. Since I'm using a database (PlanetScale + Prisma) and not a JWT strategy, the documentation found here https://next-auth.js.org/tutorials/refresh-token-rotation is not useful to me (jwt callback is never called).
As far as I'm understanding it, it means it's not really possible to check the expiration in the session callback and refresh the token here without accessing the database each time?
What can I do if I want to refresh the access token in my database? Should I use a JWT strategy instead, even though I'm using a database?
To do refresh token rotation when using a database strategy you can do something like this:
async function refreshAccessToken(session: Session) {
if (!session.user?.id) {
return;
}
const {
id,
refresh_token: refreshToken,
expires_at: expiresAt,
} = (await prisma.account.findFirst({
where: { userId: session.user.id, provider: "reddit" },
})) ?? {};
if (!id || !refreshToken) {
return;
}
// If expired refresh it
if (expiresAt && Date.now() / 1000 > expiresAt) {
const authorizationString = Buffer.from(
`${process.env?.["REDDIT_CLIENT_ID"]}:${process.env?.["REDDIT_CLIENT_SECRET"]}`,
).toString("base64");
const headers = {
Authorization: `Basic ${authorizationString}`,
"Content-Type": "application/x-www-form-urlencoded",
};
const urlSearchParams = new URLSearchParams();
urlSearchParams.append("grant_type", "refresh_token");
urlSearchParams.append("refresh_token", refreshToken);
urlSearchParams.append("redirect_uri", `${process.env?.["NEXTAUTH_URL"]}/api/auth/callback/reddit`);
const { data } = await axios.post<RedditResponse>("https://www.reddit.com/api/v1/access_token", urlSearchParams, {
headers,
});
await prisma.account.update({
where: { id },
data: {
access_token: data.access_token,
expires_at: Math.floor(Date.now() / 1000) + data.expires_in,
refresh_token: data.refresh_token,
token_type: data.token_type,
scope: data.scope,
},
});
}
}
You can use this anywhere I guess. I don't know if it makes sense to use this in the session callback or not since it's probably a performance hit, so maybe just call it each time you actually need the access token for something? I'm not knowledgable about this to know what the best practice is in this regard...
After many hours of tinkering i just found out how to get the refresh token into the database!
following the first part of the next auth token refresh tutorial, add the authorization param to the provider options
const GOOGLE_AUTHORIZATION_URL =
"https://accounts.google.com/o/oauth2/v2/auth?" +
new URLSearchParams({
prompt: "consent",
access_type: "offline",
response_type: "code",
});
and
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
authorization: GOOGLE_AUTHORIZATION_URL,
}),
This will send me well on my way to figuring out the rest of the process... hope it works for you too!
Related
I'm using the Stripe extension in Firebase to create subscriptions in a NextJS web app.
My goal is to create a link for a returning user to edit their payments in Stripe without authenticating again (they are already auth in my web app and Firebase recognizes the auth).
I'm using the test mode of Stripe and I have a test customer and test products.
I've tried
The Firebase Stripe extension library does not have any function which can just return a billing portal link: https://github.com/stripe/stripe-firebase-extensions/blob/next/firestore-stripe-web-sdk/markdown/firestore-stripe-payments.md
Use the NextJS recommended import of Stripe foudn in this Vercel blog
First I setup the import for Stripe-JS: https://github.com/vercel/next.js/blob/758990dc06da4c2913f42fdfdacfe53e29e56593/examples/with-stripe-typescript/utils/get-stripejs.ts
export default function Settings() {
import stripe from "../../stripe_utils/get_stripejs"
async function editDashboard() {
const dashboardLink = await stripe.billingPortal.sessions.create({
customer: "cus_XXX",
})
}
console.log(dashboardLink.url)
return (
<Button
onClick={() => editDashboard()}>
DEBUG: See payments
</Button>
)
}
This would result in an error:
TypeError: Cannot read properties of undefined (reading 'sessions')
Use the stripe library. This seemed like the most promising solution but from what I read this is a backend library though I tried to use on the front end. There were no errors with this approach but I figure it hangs on the await
import Stripe from "stripe"
const stripe = new Stripe(process.env.STRIPE_SECRET)
...
const session = await stripe.billingPortal.sessions.create({
customer: 'cus_XXX',
return_url: 'https://example.com/account',
})
console.log(session.url) // Does not reach here
Use a pre-made Stripe link to redirect but the user will have to authenticate on Stripe using their email (this works but I would rather have a short-lived link from Stripe)
<Button component={Link} to={"https://billing.stripe.com/p/login/XXX"}>
Edit payment info on Stripe
</Button>
Using POST HTTPS API call found at https://stripe.com/docs/api/authentication. Unlike the previous options, this optional will register a Stripe Dashboard Log event.
const response = await fetch("https://api.stripe.com/v1/billing_portal/sessions", {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
'Authorization': 'bearer sk_test_XXX',
'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify(data), // body data type must match "Content-Type" header
})
The error is I'm missing some parameter parameter_missing -customer. So I'm closer to a resolution but I feel as if I should still be able to make the solution above work.
You should use Stripe library to create a billing portal session (your 2nd approach), and you might want to check your Dashboard logs and set the endpoint to /v1/billing_portal/sessions so that you can see if there are any errors during portal session creation.
Given my case, I chose to call the API itself instead of the libraries provided:
export default async function Stripe(payload, stripeEndpoint) {
const _ENDPOINTS = [
"/v1/billing_portal/sessions",
"/v1/customers",
]
let contentTypeHeader = "application/json"
let body = JSON.stringify(payload)
if _ENDPOINTS.includes(stripeEndpoint)) {
contentTypeHeader = "application/x-www-form-urlencoded"
body = Object.keys(payload).map(
entry => entry + "=" + payload[entry]).join("&")
}
try {
// Default options are marked with *
const stripeResponse = await fetch("https://api.stripe.com" + stripeEndpoint, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
headers: {
"Authorization": "bearer " + STRIPE_PRIVATE_KEY,
"Content-Type": contentTypeHeader,
},
redirect: "follow", // manual, *follow, error
referrerPolicy: "no-referrer", // no-referrer, *client
body: body, // body data type must match "Content-Type" header
})
return await stripeResponse.json()
} catch (err) {
console.error(err)
}
}
I'm building an email marketing automation tool using NextJS, next-auth and Microsoft Graph API. I'm using next-auth's Azure AD B2C provider to authenticate users, and I've been following their docs.
Within the Configuration (Advanced) section of the docs, I've followed the steps to setup an Azure AD api app to communicate with the Microsoft Graph API (to send email on our user's behalf). Now, when a user signs up, an access_token (jwt) is added to my accounts db table. Here it is decoded:
{
"iss": "https://something.b2clogin.com/b03...f94/v2.0/",
"exp": 1664588154,
"nbf": 1664584554,
"aud": "6eb...c5b",
"idp_access_token": "EwB...QI=",
"idp": "live.com",
"name": "Will Despard",
"sub": "1f7...d6c",
"emails": [
"willdespard#outlook.com"
],
"tfp": "B2C_1_signupsignin",
"scp": "mail.send",
"azp": "ff8...f5d",
"ver": "1.0",
"iat": 1664584554
}
The problem is, there is no example of how to setup the Microsoft Graph JS Client with next-auth. For example, according to Microsoft, to create a Microsoft Graph API client, you must do the following:
import { Client } from '#microsoft/microsoft-graph-client';
const client = Client.init({
authProvider: (done) =>
done(
null,
accessToken // WHERE DO WE GET THIS FROM?
),
});
const sendMail = {
message: {
subject: 'Meet for lunch?',
body: { contentType: 'Text', content: 'The new cafeteria is open.' },
toRecipients: [
{ emailAddress: { address: 'william.cm.despard#gmail.com' } },
],
},
};
const userDetails = await client.api('/me/sendMail').post(sendMail);
However, the following is unclear:
Where are we meant to get the accessToken used in this example from? I've tried using the idp_access_token in the decoded accessToken on my accounts db table (above), but this doesn't seem to work.
I'm assuming the accessToken we use to communicate with Microsoft Graph API is going to expire after a short amount of time. How do we handle getting a new token?
Help/code examples would be much appreciated!
I would try it like this. First, it looks that for graph access you should be looking for Azure AD provider, not Azure AD B2C that is a service that provides identity providers. I.e. looks like you need this one: https://next-auth.js.org/providers/azure-ad
To use Microsoft Graph to send mail you'll also need to request a non-default scope with "Send Mail" grant from your user. Means, when authorizing your app the user will be asked to consent that your app will send emails on behalf of him. Also you'll need to save the graph access token you get from the authentication flow. Something like this:
import AzureADProvider from "next-auth/providers/azure-ad"
export const authOptions: NextAuthOptions = {
providers: [
....
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
authorization: {
params: {
scope:
"openid email profile Mail.Send",
},
},
// tenantId: process.env.AZURE_AD_TENANT_ID,
}),
],
callbacks: {
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token
},
Please note that if you do not specify tenantId that would mean that your application will be available for users from any tenant, but that in turn would mean that you must be a verified publisher (i.e. must have a valid MPN ID associated with your app). If you do specify a tenantId, then your app will only work for users from that specified tenant.
Later on, you could just use the token from the API:
import { getToken } from 'next-auth/jwt';
import { Client } from '#microsoft/microsoft-graph-client';
// some API function
export default async function handler(req, res) {
const token = await getToken({ req })
if (token) {
const accessToken = token.accessToken;
const client = Client.init({
authProvider: (done) =>
done(null, accessToken)
});
const sendMail = {
message: {
subject: 'Meet for lunch?',
body: { contentType: 'Text', content: 'The new cafeteria is open.' },
toRecipients: [
{ emailAddress: { address: 'william.cm.despard#gmail.com' } },
],
},
};
const userDetails = await client.api('/me/sendMail').post(sendMail);
...
Ok so I am using firebase as authentication for my iOS app. Now I plan on adding video calling to my app using an external service know as connectyCube. This service has their own authentication system and I cannot use their services unless a user is authenticated.
Option 1: I can use their own authentication which means my app would have two authentication systems - not very productive
Option 2: They say I can use an existing authentication to validate users
I understand that this is a common thing in the developers world and I see the word OAuth and JWT being thrown around but I am a rookie developer and I want to understand how I can use firebase and authenticate a user from an external service.
These are the questions they have asked when I opted for the "I have my own authentication" option:
What is your end point URL
Is it GET or POST
Request Headers
Request Params
Response Params
Where do I get all this information from firebase? Any help would be great
As an alternative to #Dharmaraj's answer, you could instead make use of a HTTP Event Cloud Function for this based on the code sample they've provided.
Using this method, you create the endpoint /verifyUserToken to be used by ConnectyCube.
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
export const verifyUserToken = functions.https.onRequest((req, res) => {
const idToken = req.query.token;
verifyUser(idToken)
.then(
(userData) => {
res.status(200).json(userData)
},
(err) => {
console.log("Token verification failed.", err.code || err.message);
res.status(422).json({error: "User token is invalid"})
}
)
.catch((err) => console.error("Unexpected crash", err));
});
async function verifyUser(token) {
if (!token)
throw new Error("token missing");
// using `true` here to force token to be checked against the Firebase
// Auth API rather than trusting its contents as-is
const { uid, email } = await admin.auth().verifyIdToken(token, true);
// pull the user's username from their user data
// at /users/{userId}/username
const username = (await admin.database().ref("users/" + uid + "/username")).val();
// use user's actual email if available, otherwise fallback
// to a userID based email
const uEmail = email || uid + "#users.noreply.yourapp.com";
// use user's username if available, otherwise fallback to
// the email address above.
const uLogin = username !== null ? username : uEmail;
return {
uid,
login: uLogin,
email: uEmail,
user: {id: uid, login: uLogin, email: uEmail}, // <- this part in particular is used by ConnectyCube
users: [{uid, login: uLogin, email: uEmail}]
};
}
Once deployed, you would use the following settings:
Setting
Value
API URL:
https://us-central1-PROJECT-ID.cloudfunctions.net/verifyUserToken
GET/POST
GET
Request params:
{"token": "#{login}"}
Response params:
{"uid": "#{user.id}", "email": #{user.email}, "login": "#{user.login}"}
It looks like ConnectyCube uses some sort of Session Tokens as mentioned in their documentation with their own username and password.
The most easiest way would be creating a ConnectyCube account whenever a new user signs up in your Firebase app using Firebase Auth Triggers for Cloud functions. Then you can generate username and password on behalf of your user and store them in a Database.
So whenever you need to create a ConnectyCube session, check for the currently logged in user and fetch their ConnectyCube credentials.
async function createCCSession() {
const userId = firebase.auth().currentUser.uid
const ccCrednetials = (await firebase.database().ref(`ccCreds/${userId}`).once('value')).val()
ConnectyCube.createSession(ccCredentials)
.then((session) => {
console.log(session)
return session
}).catch((error) => console.log(error));
}
You can protect the database using security rules so a user can access their credentials only.
{
"rules": {
"ccCreds": {
"$uid": {
".read": "$uid === auth.uid"
}
}
}
}
While I don't normally double-answer a question, in the course of exploring some other authentication related problems, I've managed to eliminate the Cloud Function from my other answer entirely and instead call the Authentication API directly.
Setting
Value
API URL:
https://www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo?key=FIREBASE_CONFIG_API_KEY
GET/POST
POST
Request params:
{"idToken": "#{login}"}
Response params:
{"uid": "#{users.0.localId}", "email": #{users.0.email}, "full_name": "#{users.0.displayName}"}
On your client, you just call the ConnectyCube Login API with the following data:
POST https://api.connectycube.com/login
login=<Firebase-ID-token>
password=<any-random-value-to-pass-the-validation>
In my web application, I am using Firebase for Authentication, to access any API, I have to authenticate from firebase.
Question:
How can I get access token of firebase in Postman?
I have 2 solutions for this problem:
1) Get Access Token from firebase in postman, store that access token in postman global env. variable and then I can do other API request. (Here I don't know how to get access token in postman)
2) Do the login in the browser, copy access token from network request, store it in bash_profile and then use it in Postman. (Here I don't know how to read OS env. variable)
When you want to use Postman only and don't want to build a frontend you can use this auth request in Postman: POST https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={API_KEY}
In the Body you should send the following JSON string:
{"email":"{YOUR_EMAIL_ADDRESS}","password":"{PASSWORD}","returnSecureToken":true}
Content type is application/json (will be set automatically in Postman).
You can find the Firebase API_KEY in the Firebase project settings (it's the Web-API-key).
As response you will get a JSON object and the idToken is the token you need for all your API requests as Bearer token.
To have a automated setting of this token, you can add the following code in the Tests tab at your auth request:
var jsonData = JSON.parse(responseBody);
postman.setGlobalVariable("id_token", jsonData.idToken);
For all your API requests you should set the Authorization to Bearer Token and the value for the token is {{id_token}}.
Now the token will be automatically used once you executed the auth request and got the response.
An easy way to retrieve the access token from firebase is to:
create an html file in a directory
copy in the html file the content of firebase auth quickstart
replace the firebase-app.js and firebase-auth.js as explained in firebase web setup to point them at the proper cdn location on the web
replace firebase.init script with the initialization code from your app on the console like this:
var config = {
apiKey: "my secret api key",
authDomain: "myapp.firebaseapp.com",
databaseURL: "https://myapp.firebaseio.com",
projectId: "myapp-bookworm",
storageBucket: "myapp.appspot.com",
messagingSenderId: "xxxxxxxxxxxxx"
};
firebase.initializeApp(config);
open the html file in your browser and either sign in or sign up. The Firebase auth currentUser object value should be displayed.
inspect the html and expand the quickstart-account-details element. This should have the json object displayed.
copy the content of accessToken
In postman go to authorization, select bearer token and paste the copied token in the token value field.
You should be now able to call apis that are secured by firebase auth. Keep in mind that this only gets and passes the access token so once the token is expired you may need to request a new one (steps 5 to 8)
you can also look at this
Hope this helps!
In addition of naptoon's post:
var jsonData = JSON.parse(responseBody);
postman.setGlobalVariable("id_token", jsonData.idToken);
This is "old style", which is deprecated by Postman.
The "new style" is:
pm.environment.set("id_token", pm.response.json().idToken);
go to the pre-request script and add this code (use your API_KEY, USER_EMAIL, USER_PASSWORD)
const reqObject = {
url: "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={API_KEY}", // API_KEY -> your API key from firebase config
method: 'POST',
header: 'Content-Type:application/json',
body: {
mode: 'raw',
raw: JSON.stringify({ "email": {USER_EMAIL}, "password": {USER_PASSWORD}, "returnSecureToken": true })
}
};
pm.sendRequest(reqObject, (err, res) => {
const idToken = res.json().idToken; // your idToken
pm.environment.set("FIREBASE_TOKEN", idToken ); // set environment variable FIREBASE_TOKEN with value idToken
});
this code will add the environment variable FIREBASE_TOKEN, but u can do whatever you want with idToken =)
I came across a need to do this where staging and production environments require a different Firebase idToken but local does not use one. I expanded upon naptoon's and leo's answers to use the identitytoolkit's verifyPassword endpoint as part of a pre-request:
const apiKey = pm.environment.get('api_key');
if ( ! apiKey) {
return
}
const tokenEnv = pm.environment.get('token_env')
if (tokenEnv && tokenEnv === pm.environment.name) {
const tokenTimestamp = Number.parseInt(pm.environment.get('token_timestamp'), 10)
const elapsed = Date.now() - tokenTimestamp
if (elapsed < 20 * 60000) {
return
}
}
pm.sendRequest({
url: `https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=${apiKey}`,
method: 'POST',
header: {
'Content-Type': 'application/json',
},
body: {
mode: 'raw',
raw: JSON.stringify({
email: pm.environment.get('auth_username'),
password: pm.environment.get('auth_password'),
returnSecureToken: true,
}),
},
}, function (err, res) {
let json
if ( ! err) {
json = res.json()
if (json.error) {
err = json.error
}
}
if (err) {
pm.environment.unset('auth_token')
pm.environment.unset('token_env')
pm.environment.unset('token_timestamp')
throw err
}
pm.expect(json.idToken).to.not.be.undefined
pm.environment.set('auth_token', json.idToken)
pm.environment.set('token_env', pm.environment.name)
pm.environment.set('token_timestamp', Date.now())
})
The access token is cached for a given environment for up to 20 minutes (I have not implemented refresh token). The token is cleared if the environment is different to the last request or an error occurs.
Copy the below block of code and place it in the 'pre-request scripts' tab of the request on Postman. It will automatically get a token and put it as 'Authorization' header every time you make a request. You don't need to add any header or authorization manually. You don't even need to worry about token expiry.
Obviously, replace the app api key, username and password place holders.
const postRequest = {
url: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={APP_API_Key}',
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: {
mode: 'raw',
raw: JSON.stringify({
"email": "{Your_Email}",
"password": "{Your_Password}",
"returnSecureToken": true
})
}
};
pm.sendRequest(postRequest, (error, response) => {
var jsonData = response.json();
pm.globals.set("id_token", jsonData.idToken)
});
pm.request.headers.add({key: 'Authorization', value: '{{id_token}}'})
Firebase Auth not response Access Token just idToken. you must verify/exchange with your auth system to get it.
Here is the full list apis I found for interacting with Firebase by using its API endpoint directly.
https://www.any-api.com/googleapis_com/identitytoolkit/docs/relyingparty
If your using Node here's my solution,
With the firebase admin SDK import that into your file, and follow #naptoon instructions for setting up a route in PostMan.
In Nodejs in your file put the following
const user = admin.auth().verifyIdToken(req.headers.authorization)
I tried using
const auth = getAuth() const user = auth.currentUser
and that way didn't work for me so I went with the firebase admin route which worked well with minimal code
For anyone still a bit confused, this works perfectly with Firebase using Auth emulators.
Brief Overview
Create functions
Setup emulator
Generate Token
Perform authed request(s)
1. Create functions
2 functions are required:
Generate ID Token function:
import {https} from "firebase-functions";
import {auth} from "firebase-admin";
export const generateAuthToken = https.onCall((data, _context) => {
if (!data.uid) {
return new https.HttpsError("invalid-argument", "Missing UID argument", "Missing UID argument");
}
return auth().createCustomToken(data.uid).then(value => {
console.log(`Token generated: ${value}`);
return {
status: true,
token: value
};
}).catch(reason => {
console.warn(reason);
return {
status: false,
token: ""
}
});
});
(optional) Auth'd function:
import {https} from "firebase-functions";
import {auth} from "firebase-admin";
export const checkAuthenticated = https.onCall((_data, context) => {
if (!context.auth) {
return new https.HttpsError("unauthenticated", "You need to be authenticated to retrieve this data");
}
return "Congratulations! It works.";
});
2. Setup environment
(optional) Setup emulators
Run your firebase project as you'd normally do
Postman, create 2 requests:
1. generateAuthToken
Method: POST
URL: http://127.0.0.1:5001/{project-name}/{region}/generateAuthToken
Headers:
"Content-Type": "application/json; charset=utf-8"
body (RAW: JSON)
{
"data": {
"uid":"1234567890"
}
}
2. checkAuthenticated
Method: POST
URL: http://127.0.0.1:5001/{project-name}/{region}/checkAuthenticated
Headers:
"Content-Type": "application/json; charset=utf-8"
body (RAW: JSON)
{
"data": {
}
}
Authentication Tab > Type Bearer: {insert token}
3. Generate Token
Call postman function using method described in 2.1
4. Perform authed request(s)
For every authed request, add the bearer token as described in 2.2 and it all works as expected.
I followed the Firebase's guide on how to authenticate with Github. https://firebase.google.com/docs/auth/web/github-auth
The return result from Firebase's signInWithRedirect method contains the user's displayName and email, etc. However, it doesn't seem to contain user's 'login' username which is the key for invoking most of Github's API calls.
I am sure there is a way to get it, but I just can't seem to find any documentation. Does anyone happen to know how to solve it?
I ended up using Github's API to get user's username with accessToken.
You should be able to get the user's GitHub username through a parameter called "username" (see more here: https://github.com/firebase/firebase-simple-login/blob/master/docs/v1/providers/github.md)
Note: firebase-simple-login was deprecated on October 3th, 2014
You can use get the authenticated user from this GitHub's api
Or if you use octokit javascript rest api client, you can do something like this
octokit = new Octokit({auth: userAccessToken })
octokit.users.getAuthenticated()
.then(result => {
console.log(result.data.login) // this is the username
})
Note: you'll get accessToken after GitHub <-> firebase login
Hope this is helpful!
You can get the username in additionalUserInfo:
const githubProvider = new firebaseClient.auth.GithubAuthProvider();
githubProvider.addScope('read:user');
githubProvider.setCustomParameters({
allow_signup: false,
});
firebaseClient.initializeApp(clientConfig);
async function submit() {
try {
const response = await firebaseClient
.auth()
.signInWithPopup(githubProvider);
console.log(response.additionalUserInfo);
} catch (error) {
alert(error);
}
}
You Can use email to do authorized requests insted username:
Username: mayGitHubEmail#mail.com
Password: accessToken
like this with Postman
body sent
Here is a sample using class func in Swift using Alamofire and SwiftyJSON pods:
import Alamofire
import SwiftyJSON
enum NetworkError: Error {
case url
case server
case auth
}
class GistServices {
class func makePostApiCall(toUrl path: String, withBody parameters: JSON, usingCredentials: Bool = false) -> Result<Data?, NetworkError> {
guard let url = URL(string: path) else {
return .failure(.url)
}
if let email = UserAuthSingleton.shared.get(), let password = UserAuthSingleton.shared.getUserToken() {
var result: Result<Data?, NetworkError>!
var request = AF.request(url, method: .post, parameters: parameters)
if(usingCredentials){
let credentialData = "\(email):\(password)".data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue))!
let base64Credentials = credentialData.base64EncodedString()
let headers = [HTTPHeader(name: "Authorization", value: "Basic \(base64Credentials)"),
HTTPHeader(name: "Accept", value: "application/json"),
HTTPHeader(name: "Content-Type", value: "application/json")]
request = AF.request(url, method: .post, parameters: parameters.dictionaryValue, encoder: JSONParameterEncoder.default, headers: HTTPHeaders(headers))
}
request
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.response { (response) in
switch response.result {
case .failure(_):
result = .failure(.server)
case .success(let value):
result = .success(value)
}
}
return result
}
return .failure(.auth)
}
}