Just trying to learn yet another framework here :D
I'm using Nuxt with Strapi and MongoDB. For cutting the corners (dont' hate the player..) I've installed the Nuxt/Strapi module => https://strapi.nuxtjs.org/
I get the collection data from Strapi ok when I'm not authenticated, but when the user is authenticated (basic strapi roles), then Strapi throws 401 error for the collection data. The collection is open for public aswell as for authenticated users in roles & permissions. So there should not be issues from Strapi end.
I get and can log the JWT token from Strapi and the user is logged in/authenticated properly. To my best knowledge (it's kinda badly documented) the Nuxt/Strapi module should pass the JWT token in the header and authentication should work out of the box for REST calls? At least the code works when not authenticated:
async fetch() {
this.building = await this.$strapi.find('building');
},
data() {
return {
building: []
}
}
When authenticated the console logs these errors:
index.js?d823:434 GET http://localhost:1337/building 401 (Unauthorized)
fetch.client.js?2293:75 Error in fetch(): HTTPError: Schema hasn't been registered for model "user".
Use mongoose.model(name, schema)
I'm new to this stack so I'm kinda lost what's wrong.
The onlyt thing I could find close to related to this issues was this thread:
Mongoose Schema hasn't been registered for model
This is very basic functionality and must of been tested, so I don't think that this kinda bug can slip into the module. It's more likely I'm just doing somtething wrong.
Thanks in advance!
You must first authenticate, or use a public user, for authenticate first call /auth/local you will get a token in response, use it in the headers of your fetch.
check this:
import axios from 'axios';
const { data } = await axios.post('http://localhost:1337/auth/local', {
//your user
identifier: 'reader#strapi.io',
password: 'strapi',
});
console.log(data);
to fetch
import axios from 'axios';
const { data } = await axios.get('http://localhost:1337/articles', {
headers: {
Authorization:
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTc2OTM4MTUwLCJleHAiOjE1Nzk1MzAxNTB9.UgsjjXkAZ-anD257BF7y1hbjuY3ogNceKfTAQtzDEsU',
},
});
change the long token for the previous saved token
Related
I am creating React Native app using Expo and used its inbuilt Facebook.useAuthRequest to generate a response when a user logs in. When I create a response type of Token I am able to take this token and send it to my backend API that successfully uses it to get the user details.
However I had hoped to implement a response type of code and use this on the backend API generate the access Token and then request the user details - as I believe this is the most secure option when sending the code to my server.
The issue that I'm facing is that I keep getting an error when trying to formulate the requst to Graph API and I dont understand why:
error: {
message: 'Missing client_id parameter.',
type: 'OAuthException',
code: 101,
fbtrace_id: 'ARHcoh260kBwj7l9yDHjU-n'
}
I just want to confirm that I believe I have inserted all the correct information into the request, so I am unsure of why this error is saying its missing the cliend_id. Here is my request from my API server:
const { data } = await axios({
url: https://graph.facebook.com/v12.0/oauth/access_token? client_id=${appId} &redirect_uri=${redirectUri} &client_secret=${appSecret} &code=${code},
method: 'get',
});
I just want to confirm that the client_id I have taken from app id I created on the facebook developer page, as well as the client_secret, redirect is the https:// used in the initial request and the code is the code initially received in my client side request.
Thanks in advance for any suggestions :)
Just a quick update on this, I was ablel to reformat the request as I believe it had some errors in the spacing and I moved to using the .env files so now my request looks like this:
const redirectUri = {MY_REDIRECT URL};
const appId = process.env.FACEBOOK_CLIENT_ID;
const appSecret = process.env.FACEBOOK_CLIENT_SECRET;
const { data } = await axios({
url: `https://graph.facebook.com/v12.0/oauth/access_token?client_id=${appId}&redirect_uri=${redirectUri}&client_secret=${appSecret}&code=${code}`,
method: 'get',
});
It seems I have moved onto a new error with the following:
error: {
message: 'Invalid code verifier. Code verifier should be a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long.',
type: 'OAuthException',
code: 1,
fbtrace_id: 'AQKIUad5RRCitb6m977fnFW'
}
I'm a bit stumped for what this means as I have checked and all my values appear correct. My only thought is if I need to do something with the code initially received on the client side?
Ok so I finally figures it out - the issue was the I wasn't sending the code_verifier along with my request to exchange the Auth Code for a token. I ended up sending this code_verifier to my API server then adding this to the request so it looked something like this:
FB.api(
'oauth/access_token',
{
client_id: appId,
client_secret: appSecret,
redirect_uri: redirectUri,
code_verifier: code_verifier,
code: code,
},
function (response) {
if (!response || response.error) {
console.log(!response ? 'error occurred' : response.error);
return;
}
var accessToken = response.access_token;
This then finally gave me the accessToken I was looking for that I can then use to exchange for user details server side.
... and the code_verifier is obtained from request.codeVerifier.
const [request, response, promptAsync] = Facebook.useAuthRequest(...
I've seen many posts on Stack that are close to what I need, but don't fully answer my question (I'm pretty green with Node). I'm working on connecting a Twitch/Tiltify donation campaign to a Raspberry Pi via Node.js/Axios. I'd like the Pi to regularly check for new donations, then activate physical circuits (solenoid valves etc.) to be viewed live on the stream. Here's my code so far:
const axios = require('axios');
axios.get('URL_GOES_HERE', {
headers: {
'Authorization' : 'Bearer MY_TILTIFY_ACCESS_TOKEN'
}
})
.then(response => {
console.log(response.data.url);
console.log(response.data.explanation);
})
.catch(error => {
console.log(error);
});
I assume that MY_TILTIFY_ACCESS_TOKEN is the access token I generated from within my Tiltify account. I'm confused, however, about what value to put in URL_GOES_HERE. The somewhat sparse Tiltify API docs give two possible URLS: https://tiltify.com/oauth/authorize and https://tiltify.com/oauth/token. Or am I supposed to put my bearer credentials directly into the URL of a useful request, like https://tiltify.com/api/v3/user? I've tried all three, and I just get undefined undefined in the console.
A nudge in the right direction is appreciated! Thanks for your time.
#benstepp over on Github ultimately answered my question. Here's the code he provided:
const axios = require('axios');
axios.get('https://tiltify.com/api/v3/campaigns/MY_CAMPAIGN_ID/rewards', {
headers: {
'Authorization' : 'Bearer MY_API_TOKEN'
}
})
.then(response => { // this is an axios response object (https://github.com/axios/axios#response-schema)
//console.log(response.data); // this is the response body from tiltify (https://tiltify.github.io/api/endpoints/campaigns-id-donations.html)
//console.log(response.data.data); // this is the .data property of our responses
response.data.data.map((reward) => {
// the name/amount of the recent donations
console.log(`${reward.name}`)
})
})
.catch(error => {
console.log(error);
});
The /authorize endpoint is used for the Web Server OAuth Authentication Flow and User-Agent OAuth Authentication Flow.
The /token endpoint is used for the Username-Password OAuth Authentication Flow and the OAuth Refresh Token Process.
So first you need to get Authorized to be able to use Tiltify api. For that you need to use either of the flow
https://tiltify.com/oauth/authorize
https://tiltify.com/oauth/token
Assuming you used token route, you will get a response something like this:
{ "access_token":"token", "token_type":"bearer", "refresh_token":"refresh_token" }
Then using the access_token you got from the response you will call the api routes so in URL GOES HERE will be your api routes like
/campaigns/:id
causes/:id
with which you'll use Authorization: Bearer <access_token> in headers
I would like to implement auto refresh jwt token before every request to GraphQL with Apollo middleware in React Native app. After every user login he gets two tokens: access and refresh. Access token it is the short one for 30-60 min for using in authorization header. And refresh token it is the long one for 60 days for confirm of refresh token graphql mutation.
My flow:
User login and gets 2 tokens -> put access token to authorization header with Appollo setContext.
User make request to GraphQL -> check expireTime of accessToken on a client side:
-> if it is not expired -> confirm request
-> if it is has expired -> call GraphQL refreshToken mutation -> get new tokens -> confirm request.
For keeping tokens on the client side i use KeyChain storage. Can you tell me please should i use Apollo cache for keeping tokens too? Should i write Apollo state for tokens? And how i can implement my flow?
GraphQL mutation
mutation UpdateTokens($refreshToken: String!, $refreshTokenId: String!)
{
updateTokens(refreshToken: $refreshToken, refreshTokenId: $refreshTokenId) {
user {
name
phone
}
accessToken
refreshToken
}
}
App.js
import React from 'react'
import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link'
import { ApolloProvider } from 'react-apollo'
import { ApolloProvider as ApolloHooksProvider } from 'react-apollo-hooks'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import * as Keychain from 'react-native-keychain'
import AppNavigator from './AppNavigator'
const httpLink = createHttpLink({
uri: 'http://localhost:4000'
})
const cache = new InMemoryCache()
const authLink = setContext(async (req, { headers, ...context }) => {
const tokens = await Keychain.getGenericPassword()
const accessToken = tokens.username
return {
headers: {
...headers,
authorization: accessToken ? `Bearer ${accessToken}` : ''
},
...context
}
})
const client = new ApolloClient({
link: ApolloLink.from([authLink, httpLink]),
cache,
connectToDevTools: true
})
const App = () => {
return (
<ApolloProvider client={client}>
<ApolloHooksProvider client={client}>
<AppNavigator />
</ApolloHooksProvider>
</ApolloProvider>
)
}
export default App
I honestly think you are on the right path -- as I read your required functionality I thought of apollo-link-context and was happy to see you were taking that approach. We recently had to implement similar functionality in a react-native app, which entailed attaching custom headers with authentication-related data. To retrieve this data we were required to make an async request, although ours was over the network to a third-party service. We did this all in setContext from the client just as you are. This worked well.
I do not think you need to concern yourself with updating your Apollo cache with your tokens, manually or otherwise, for at least a few reasons. First, given the quasi-sensitive nature of what you are storing, it would be best practice to defer to the more secure storage solution, which in this case is likely the Keychain. In addition, having a single source of truth for the tokens in your application can keep things clean.
Assuming you do not want to write this data to cache, I would double-check Apollo client is not automatically doing so. For example, if you for some reason previously queried your token data, Apollo might automatically update your cache upon receiving the mutation payload. Just something to be mindful of.
I have created a Node.js express server that connects to Salesforce.com using the SOAP interface provided by 'jsforce'. It uses session cookies for authorization via the 'express-session' package. So far, it has a POST method for login and a GET to perform a simple query. Testing with Postman has proven that this server is working as expected.
As the browser interface to this server, I have wrttien a Vue application that uses axios to perform the GET and POST. I need to save the session cookie created during login POST then attach attach the cookie to subsequent CRUD operations.
I have tried various methods to handle the cookies. One method I have tried is using axios response interceptors on the POST
axios.interceptors.response.use(response => {
update.update_from_cookies();
return response;
});
The function 'update_from_cookies' attempts to get the cookie named 'js-force' but it does not find it although I know it is being sent
import Cookie from 'js-cookie';
import store from './store';
export function update_from_cookies() {
let logged_in = Cookie.get('js-force');
console.log('cookie ' + logged_in);
if (logged_in && JSON.parse(logged_in)) {
store.commit('logged_in', true);
} else {
store.commit('logged_in', false);
}
}
I have also seen various recommendations to add parameters to the axios calls but these also do not work.
I would appreciate some advice about how to handle cookies using axios or some similar package that works with Vue
Thanks
The problem has been resolved. I was using the wrong syntax for the axios call
The correct syntax has the {withCredentials: true} as the last parameter
this.axios.post(uri, this.sfdata, {withCredentials: true})
.then( () => {
this.$router.push( {name : 'home' });
})
.catch( () => {
});
I am currently working on a authentication service for a node.js microservices application using typescript, WebStorm, passport and jwt. While trying to add the route to "/api/login", I am noticing that the intellisense does not seem to pick up the user object of req.user or the authorization object of req.header.authorization. For example, the following method is not working because it can not find the user object:
private generateToken(req: Request, res: Response, next: NextFunction){
req.token = jwt.sign({
id: req.user.id,
firstname: req.user.firstname,
lastname: req.user.lastname,
roles: req.user.roles
}, process.env.AUTH_KEY, {
expiresIn: "7d"
});
return next();
}
I am using the Request object from express:
import { NextFunction, Request, Response, Router } from "express";
Would I need to use a different Request object?
Also, if I need to force authentication to certain api routes but lock other routes down, how should this be done using passport-jwt? I know there is an express-unless package that I can use for express-jwt.
Not sure why this question downvoted, maybe because it should be two separate questions.
You can extend type declarations for Express.
Extending the express type definitions
Add a file library-ext.d.ts into your source directory with this.
declare module 'express' {
export interface Request {
user?: any;
}
}
For req.header.authorization try: req.headers['authorization']. Notice the 's'.
Relating to general authentication
It depends whether your registered users can also use the guest routes. If you never need identity on the guest routes then just register the passport authentication middleware only on the authenticated routes, or split the routes into separate routers. That's fairly simple to do, just search stack overflow or look in the docs for it.
The more complicated case is when you need both authenticated and non-authenticated users to access a route - think either a guest or authenticated customer adding something to a cart. Unfortunately passport-jwt rejects with a 401 when a token is not in the authorzation header, so the easiest way I found, rather than forking the project or rolling my own strategy, was to use middleware to add a known value to represent an otherwise anonymous request. Then just make sure that middleware is before the passport authentication in the affected routes. Here's a snippet to get you going:
CoreCtrl
class CoreCtrl {
simulateAnonymous(req, res, next) {
if (!req.headers.authorization) {
req.headers.authorization = 'Bearer guest-token';
}
return next();
}
}
Then somewhere in your Express Setup
setupRouters() {
// the public and admin routers are bound to the application
const coreCtrl = new CoreCtrl(this.serverOpts);
const anonymousCtrl = coreCtrl.simulateAnonymous.bind(coreCtrl);
this.routers.admin.use(anonymousCtrl);
this.routers.admin.use(passport.authenticate('UserBearer', { session: false }));
this.routers.public.use(anonymousCtrl);
this.routers.public.use(passport.authenticate('CustomerBearer', { session: false }));
}
Note that I had separate routers for public and admin set up here, that's not necessary but just to illustrate how to do it.
Then in the bearer strategy, you would have some code similar to this.
/**
* Run the strategy
*
* #param token {String} The JWT Token
* #param done {Callback} Callback function
*/
exec(token:string, done):Promise<any> {
// this is the workaround to support not passing a token for guest users.
if (token === 'guest-token') {
return done(null, {
userId: 'guest',
roles: ['guest']
});
}
// otherwise decode the token and find the user.
}
Finally, in some later Middleware you can check if the 'guest' role has access to the protected resource. I'd recommend acl module to manage role-based ACL list.
Property 'isAuthenticated' does not exist on type 'Request'.
Google lands here for that search, and the solution is to use Request/Response types in Express. My code was defaulting to the Fetch API's Request/Response definitions.
import { Express, NextFunction, Request, Response } from 'express'