POST request issues to Twitter API - javascript

I have been using NextJS, Twitter-lite to build an application using the twitter app, I basically am trying to implement the functionality in which users can post a tweet from the app to their twitter accounts. I have also used Next-Auth for implementing oAuth for twitter.
So after some working, I have managed to make it work and highlight my error - Access tokens. I want to fetch access tokens from the user after they have logged in,
In the below example - I run this script and a post is updated to my account indeed
Here is my code for the api/twitter/post.js
import Twitter from 'twitter-lite';
import { getSession } from 'next-auth/react'
import { getToken } from 'next-auth/jwt';
export default async (req, res) =>{
var Twit = require('twit')
const session = await getSession({ req });
const token = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET
});
console.log(token.twitter.accessToken)
var T = new Twit({
consumer_key: process.env.TWITTER_CONSUMER_KEY,
consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
access_token: 'process.env.TWITTER_ACCESS_TKN',
access_token_secret: 'process.env.TWITTER_ACCESS_TKN_SCRT'
});
T.post('statuses/update', { status: 'hello worldj!' }, function(err, data, response) {
console.log(data)
})
return res.status(200).json({ status: 'ok'});
}
Now this does work, when I hit my api route a new tweet is posted but only to 'my' account because I provided my access_tokens, I would like to know how I can get users access tokens, I have already signed them in using NextAuth so I'm sure I'm just missing a few things.

So I figured this one out,
In the call backs functions in the [...nextauth].js, I was just fetching the access_tokens with an outdated name and the new name is account.oauth_token
For anyone who ever encounters this issue - (Trying to fetch logged in users access tokens to access restricted twitter endpoints)
callbacks: {
async jwt({ token, user, account = {}, profile, isNewUser }) {
if (account.provider && !token [account.provider]) {
token[account.provider] = {};
}
if(account.oauth_token) {
token[account.provider].accessToken = account.oauth_token;
}
if (account.oauth_token_secret) {
token [account.provider].refreshToken = account.oauth_token_secret;
}
return token
},
This is how you get the logged in users access_token and access_token_secret

Related

AWS - Get user token from an existing Cognito User Pool with username and password on an existing React web app

I have a simple React web app created from create-react-app.
I also have an existing user pool (set up by a third party) on Amazon Cognito. I have been given a username and password for authentication. Once authenticated, Cognito provides a JWT token.
What I want to achieve is to authenticate the user and get a JWT access_token within the componentDidMount method of the App component; then use the token to call other APIs to retrieve some data and then show the data on the App component.
The following info is known:
region
USER_POOL_ID
APP_CLIENT_ID
USER_ACCOUNT
USER_PASSWORD
AWS provides the authenticate_and_get_token function for Python developers. Is there an equivalent for JavaScript and React developers? Any sample code is appreciated.
Don't know about the authenticate_and_get_token you mentioned, couln't find this function in Boto3 docs.
But I do know different function to retrieve a token called adminInitiateAuth. This is function available for JS.
you can implement this using Lambda, like this:
const AWS = require('aws-sdk');
const USER_POOL_ID = "us-east-1_*******";
const APP_CLIENT_ID = "***********";
const USER_ACCOUNT = "***********";
const USER_PASSWORD = "***********";
const cognitoClient = new AWS.CognitoIdentityServiceProvider({
apiVersion: "2016-04-19",
region: 'us-east-1'
});
exports.handler = async (event) => {
try {
const userData = await userSignIn(USER_ACCOUNT, USER_PASSWORD);
return userData // refer to userData.AuthenticationResult.IdToken;
} catch(e){
throw e;
}
};
const userSignIn = async (username, password) => {
try {
var params = {
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId: APP_CLIENT_ID ,
UserPoolId: USER_POOL_ID,
AuthParameters: {
USERNAME: username,
PASSWORD: password
}
};
return await cognitoClient.adminInitiateAuth(params).promise();
} catch (err) {
return err.message ? err : {message : err};
}
};
Please make sure you have the specific permission to invoke this method, like "Action": "cognito-idp:AdminInitiateAuth", in the lambda permissions, or in IAM role.

Sending jwt through header response with resolver mutation GraphQL and Appollo server

Today I'v been trying to send a jwt token back to the client via a header.
Sadly I cant get it to work, my current code looks like this.
the resolver/mutation
// log user in
Login: async(parent, args, context, info) =>{
const LoginUser = await user.findOne({username: args.username})
if (LoginUser.password == args.password){
//loginsucces
const token = jwt.sign({id: user.id}, process.env.TOKEN_SECRET);
context.res.header = ("auth", token)
LoginUser.token = token;
return LoginUser;
}else{
return LoginUser;
}
}
app.js
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ res }) => ({
res
})
});
For some reason the code doesnt recognise .header so it wont send the token. Currently i send it via the token field form the user. But that takes up space in my mongodb, and destroys the whole purpose of jwt.
Am I just forgetting a function or not getting it?

Using ably.io JWT with Angular

I'm trying to use ably.io with Angular and Azure Functions using the JWT way of authenticating since it's secure, but I'm having issues with configuring the angular side of it. The use case is for a live auction site to update bids in realtime. There isn't a specific angular tutorial for this so I'm trying to piece it together. Also this code
realtime.connection.once('connected', function () {
console.log('Client connected to Ably using JWT')
alert("Client successfully connected Ably using JWT auth")
});
never throws the alert so I don't think it's working right. I used to have it working where I wasn't using ably JWT, but had the API key on the client-side in a component like this
let api = "<api key>";
let options: Ably.Types.ClientOptions = { key: api };
let client = new Ably.Realtime(options); /* inferred type Ably.Realtime */
let channel = client.channels.get(
"auctions"
);
and I could subscribe to that channel and update auctions accordingly by their id inside ngOnInit()
channel.subscribe(message => {
const auction = this.posts.find(action => {
return action.id === message.data.auctionId;
});
if (auction) {
auction.currentBid = message.data.lastBid;
}
});
but I need to switch this logic for JWT and somehow feed that JWT token into different components as well.
Ably.io JWT tutorial reference
I put the following in my angular login service
login(email: string, password: string) {
const authData: AuthDataLogin = { email: email, password: password };
return this.http
.post<{
token: string;
expiresIn: number;
userId: string;
}>(environment.azure_function_url + "/POST-Login", authData)
.pipe(takeUntil(this.destroy)).subscribe(response => {
//JWT login token. Not Ably JWT Token
const token = response.token;
this.token = token;
if (token) {
console.log('Fetching JWT token from auth server')
var realtime = new Ably.Realtime({
authUrl: "http://localhost:7071/api/AblyAuth"
});
realtime.connection.once('connected', function () {
console.log('Client connected to Ably using JWT')
alert("Client successfully connected Ably using JWT auth")
});
...
}
With my azure function already configured, When I login, the browser console outputs
GET wss://realtime.ably.io/?access_token=<token was here>&format=json&heartbeats=true&v=1.1&lib=js-web-1.1.22
SO it returns my token, but
the alert never happens
I'm not sure how to grab that JWT token that's returned to the browser. I was thinking I could store it in localStorage to share between components and clear out localStorage when user logs out, but I need to be able to subscribe to response and assign the token to a variable, but I didn't see in ably javascript tutorial how to get variable assigned to JWT Token response since it's being called with this syntax.
I appreciate any help with this!
var realtime = new Ably.Realtime({
authUrl: "http://localhost:7071/api/AblyAuth"
});
My azure function looks like
const checkAuth = require('../middleware/check-auth');
var jwt = require("jsonwebtoken")
var appId = '<APP ID>'
var keyId = '<key ID>'
var keySecret = '<key secret>'
var ttlSeconds = 60
var jwtPayload =
{
'x-ably-capability': JSON.stringify({ '*': ['publish', 'subscribe'] })
}
var jwtOptions =
{
expiresIn: ttlSeconds,
keyid: `${appId}.${keyId}`
}
console.log("JwtPayload");
console.log(jwtPayload);
console.log("jwtOptions");
console.log(jwtOptions);
module.exports = function (context, req) {
console.log("INSIDE ABLY AUTH")
// checkAuth(context, req);
console.log('Sucessfully connected to the server auth endpoint')
jwt.sign(jwtPayload, keySecret, jwtOptions, function (err, tokenId) {
if (err) {
console.log("ERR")
console.log(err)
console.trace()
return
}
context.res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate')
context.res.setHeader('Content-Type', 'application/json')
console.log('Sending signed JWT token back to client')
console.log(tokenId)
context.res = {
status: 200,
body: JSON.stringify(tokenId),
headers: {
"Access-Control-Allow-Credentials": "true",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Set-Cookie",
"Access-Control-Max-Age": "86400",
"Vary": "Accept-Encoding, Origin",
"Content-Type": "application/json"
}
};
context.done();
})
}
I'd recommend if you're wanting to intercept the JWT prior to passing it to Ably (so as to verify the contents, and also use the JWT for other components), you make use of authCallback instead of authUrl. You can use a function instead of a direct URL, within which you can call the endpoint, and do anything you like with the response, prior to passing the JWT back to the Ably constructor. I've made a JavaScript example of using the authCallback for normal Token Authentication, but the same principle applies.
As to why you're not seeing the alert, it looks like you're sending an invalid JWT for what Ably is expecting, and thus you're not successfully connecting to Ably. For example, you're specifying 'expiresIn' rather than 'exp'. For a token to be considered valid, it expected certain elements in a very specific structure, see the documentation. I'd recommend for this sort of situation where you're not certain what's breaking that you make use of verbose logging, which you can enable in the connection constructor as "log": 4.

How do i store jsonwebtoken on cookie for front-end so client can send back the token for auth

I've been struggling to do this for about 6 days...
Everything is working perfectly such as authorization but one problem I had is making authentication.
On my user model (for creating the database schema) I do have a way to generate a token for logged in users or registered.
userSchema.methods.generateAuthToken = function(){
const token = jwt.sign({ _id: this._id }, config.get('jwtPrivateKey'));
return token;
}
So when user post to /login, server will respond with a token:
router.post('/', async (req, res) =>{
// Here i'm validating data and then if everything is right the code under will run.
console.log('logged in as: ' + user.username);
// Here i'm using the function to generateAuthToken().
const token = user.generateAuthToken();
console.log("Token from server: " + token);
// now here is my main problem i would like to use cookies to store it for an hour or so.
// then client can send it back to server for protected route.
res.status(200).send(token);
});
I have made a middleware function for auth (to check the token if you're going through a protected route)
module.exports = function (req, res, next){
// instead of using headers i would like to check for the cookie value if it's the token,
// pass the user in, else Access denied.
// I have no idea how to use cookie parser with middleware functions.
const token = req.header('x-auth-token');
if(!token) return res.status(401).send('Access denied. Sign in or register.');
try{
const decoded = jwt.verify(token, config.get('jwtPrivateKey'));
req.user = decoded;
next();
}
catch(err){
res.status(400).send('Invalid Token!');
}
}
here i'm using the auth middleware function:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
// but it's actually not passing the user in since i haven't done it with cookies.
router.get('/', auth, (req, res) =>{
res.render('index', {});
});
I do know I can do it with localStorage but it's a terrible practice and it would be better to store it on cookies so no one could hack on.
Is there any good approach to solve this problem? I'm kinda lost and lost hope to go back to sessionID (which I don't want to :( ).
After you request on frontend, you need get the response (token) and save on browser using this for example:
fetch('http://your-api-host/login', {
method: 'POST',
body: {
username: "user1",
password: "passworduser"
}
})
.then((res) => res.text((res)))
.then((token) => {
document.cookie = `AUTH_API=${token}`; <-- this save the cookie
})
With this value saved on frontend you need send this information on all requests, it's commum send this value on your HEADER (how you makes), to save on header you need read the value from token and put on header, like this:
const headersTemp = document.cookie.split(';'); // <-- this get all cookies saves and splits them in the array.
const finalHeaders = {};
headersTemp.forEach((header) => { // <-- looping on all cookies
const headerTemp = header.split('='); // <-- split each cookie to get key and value
finalHeaders[headerTemp[0].trim()] = headerTemp[1].trim() // <-- save on object to access using keys.
})
Now you can access all cookies using the key (the same used before), I used the key AUTH_API to save my cookie, let's send the request using fetch api:
fetch('http://your-api-host/route-protected', {
method: 'POST',
headers: {
'x-auth-token': finalHeaders['AUTH_API']
},
})
If you creating your application using libraries how React or any SPA framework, probably you will use tools like Axios, and I recommend uses libraris how This, it's more easy to work with cookies.

Using a separate Google account for backend processes in MeteorJS

I'm creating an application for YouTube that utilizes some of the Analytics APIs for Content Owners. The APIs require a user with sufficient permissions to be logged in, who can then retrieve reports for all the users of our application.
Currently our application can get YouTube User IDs, which is fine, but we need a separate account (other than the current user) to make requests to the API using the logged in user's ID.
How can I implement such a setup? I know it would involve using offline authentication and periodically refreshing the access tokens, but I'm not quite sure how to do it.
I've done a Google Analytics dashboard that refreshes the token in an interval. The admin chooses the GA Profile and it plots things. I needed to use a bunch of stuff to do that:
Npm Integration - So easy to use. Just take a look how to make methods calls sync.
google-api-nodejs-client [alpha] - Integrate it with the Npm above. It refreshes the token for you automatically when you Make Authenticated Requests
If you don't want to use google-apis-nodejs-client to refresh your token, you can use this code I've made to refresh the token by yourself:
var googleAccount = Accounts.loginServiceConfiguration.findOne({service: 'google'});
CLIENT_ID = googleAccount.clientId;
CLIENT_SECRET = googleAccount.secret;
REDIRECT_URL = '/_oauth/google?close';
var googleapis = Meteor.require('googleapis'),
OAuth2Client = googleapis.OAuth2Client,
client = getClient();
function getClient () {
var client = Meteor.sync(function (done) {
googleapis.discover('analytics', 'v3').execute(function (err, client) {
done(err, client);
});
});
if (client.err)
throw new Meteor.Error(400, 'Client not received');
return client.result;
}
function getOAuth2Client (user) {
var accessToken = user.services.google.accessToken,
refreshToken = user.services.google.refreshToken,
oauth2Client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
if (user.services.google.expiresAt < +(new Date())) {
var res = Meteor.http.call("POST", "https://accounts.google.com/o/oauth2/token",
{params: {
grant_type : 'refresh_token',
refresh_token : refreshToken,
client_id : CLIENT_ID,
client_secret : CLIENT_SECRET
}, headers: {
"content-type": "application/x-www-form-urlencoded"
}});
accessToken = res.data.access_token;
Meteor.users.update({_id: user._id}, {$set: {
'services.google.accessToken': accessToken,
'services.google.expiresAt': +(new Date()) + (1000 * res.data.expires_in)
}});
}
oauth2Client.credentials = {
access_token: accessToken,
refresh_token: refreshToken
};
return oauth2Client;
}
Meteor.methods({
'getAccounts': function () {
var user = Meteor.users.findOne({_id: this.userId}),
oauth2Client = getOAuth2Client(user),
accounts = getAccounts(oauth2Client, client);
return accounts;
}
});

Categories