I am trying to wrap my head around RXJS and had a quick question on how to tackle the following workflow using observables instead of promises.
Here is the log in workflow in angular 2:
i look in local storage for JWT. if JWT exists i return the token.
if not in local storage i check to see the platform. if Android i log into my server using google bearer token, my server returns a JWT (through angular HTTP obserable).
if not in local storage and the platform is a windows computer i generate a JWT on my server and return (through angular HTTP obserable)
for the two calls to the server i want to cache the token in local storage before returning the JWT information to the calling function.
I have everything execpt how to do this properly using RXJS, mostly how to chain all of this together. Can i get a quick pseudocode using Observables on how to do this? I basically want to intercept the JWT and store in local storage before moving on in the application (the calling function subscribing to the sequence above)
Any help would be great!
I would prefer async functions for this task.
var jwtPromise = null;
function getJwt() {
return jwtPromise || (jwtPromise = getJwtNotCached());
}
async function getJwtNotCached() {
const localJwt = getLocalJwt();
if (localJwt) {
return localJwt;
}
const newJwt = await fetchJwtByPlatform();
storeLocalJwt(newJwt);
return newJwt;
}
async function fetchJwtByPlatform() {
if (platformIsAndroid()) {
return await fetchJwtOnAndroid();
}
return await fetchJwtOnWindows();
}
...
This code will even ensure that no multiple network requests are made if called twice at a time.
Related
I am trying to figure out what is the best way to prevent random people access/make requests to the server.
Currently, everyone that have the url and the endpoint can easily make requests and I believe it's kind of security breach.
I am using Express.js that hosting static build of React.js
Here is an example of a call:
// Client:
async function getData(id){
try {
const res = await axios.get(`${backendDomain}/data/${id}`)
const data = res.data
return data
} catch (error) {
...
}
}
// Server:
app.get('/data/:id', function(req, res) {
...logic
res.send(data);
});
I tried adding to the Client "x-api-key" header and pass an api key that only I have and add a middleware that will check the api and see if it passed currectly from the client. But obviously it is not a good solution because you can see the key on "Network" section while inspecting.
What can I do?
Looking for the best way to prevent random people accessing the data
I am using express to create my firebase functions, and I understand how to create regular callable functions. I am lost however on the exact way to implement trigger functions for the background (i.e. onCreate, onDelete, onUpdate, onWrite), as well as how Reactjs in the frontend is supposed to receive the data.
The scenario I have is a generic chat system that uses react, firebase functions with express and realtime database. I am generally confused on the process of using triggers for when someone sends a message, to update another user's frontend data.
I have had a hard time finding a tutorial or documentation on the combination of these questions. Any links or a basic programmatic examples of the life cycle would be wonderful.
The parts I do understand is the way to write a trigger function:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite((change, context) => {
// Only edit data when it is first created.
if (change.before.exists()) {
return null;
}
// Exit when the data is deleted.
if (!change.after.exists()) {
return null;
}
// Grab the current value of what was written to the Realtime Database.
const original = change.after.val();
console.log('Uppercasing', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return change.after.ref.parent.child('uppercase').set(uppercase);
});
But I don't understand how this is being called or how the data from this reaches frontend code.
Background functions cannot return anything to client. They run after a certain event i.e. onWrite() in this case. If you want to update data at /messages/{pushId}/original to other users then you'll have to use Firebase Client SDK to listen to that path:
import { getDatabase, ref, onValue} from "firebase/database";
const db = getDatabase();
const msgRef = ref(db, `/messages/${pushId}/original`);
onValue(msgRef, (snapshot) => {
const data = snapshot.val();
console.log(data)
});
You can also listen to /messages/${pushId} with onChildAdded() to get notified about any new node under that path.
Environment:
This is code I'm trying to implement on top of the tried and tested React-boilerplate.
While this boilerplate is wonderful, it doesn't contain authentication. I'm trying to implement my own authentication logic in a way that meshes with the existing logic of the boilerplate.
Context:
I have a service/utility function that is used to send the server requests with a JWT authentication header and return its response.
Goal:
I'm trying to implement logic where when a request is made using an expired token, the access token is automatically refreshed before the request is sent to the server.
What's stopping me:
I have a saga that handles the access token refresh. It works perfectly when it is called from within a container. But because this is a service, it is unaware of the redux store. For this reason I am unable to dispatch actions.
The react-boilerplate structure works by injecting reducers and sagas on the fly. This means I'd need to inject the authentication saga and reducer from within the service (kinda feels wrong no?). However, the saga and reducer injectors are React side-effects and can, therefore, only be used inside of React components.
I feel like the task I'm trying to achieve is quite trivial (and I'm sure it was implementing a million times already), and yet, I can't think of a solution that doesn't seem to go against the entire logic of why use Redux or Sagas to begin with.
Can anyone offer some insights? In the attached image, the red text is the part I'm struggling to implement
See code below:
/**
* Requests a URL, returning a promise
*
* #param {string} url The URL we want to request
* #param {object} [options] The options we want to pass to "fetch".
*
* #return {object} The response data
*/
export default function request( url, options ) {
const token = makeSelectAccessToken();
if (!token) throw new Error('No access token found.');
// Refresh access token if it's expired.
if (new Date(token.expires) - Date.now() <= 0) {
// TODO: Attempt to use refresh token to get new access token before continuing
/** dispatch(REFRESH_ACCESS_TOKEN) **/
// PROBLEM: can't dispatch actions because the store is not exposed to this service.
// Secondary challenge: Can't inject Saga or Reducer because the React-boilerplate injectors are React side-effects.
}
options = {
...options,
Authorization: `Bearer ${token.token}`, // Adding the JWT token to the request
};
return fetch(url, options)
}
There is multiple way to do that
1- Export the store
create new store.js
import { createStore } from 'redux';
import reducer from './reducer';
//you can add your middleware, sagas, ...
const store = createStore(reducer);
export default store;
import it to your service and use it everywhere but you’ll end up with a single store for all of your users
Cons: you’ll end up with a single store for all of your users(if
your app is using Server Side Rendering don't use this method)
Pros: Easy to use
2- You can add your func to middleware and intercept an action
edit your create store code and add new middleware for authorization
const refreshAuthToken = store => next => action => {
const token = makeSelectAccessToken();
if (!token) throw new Error('No access token found.');
if(new Date(token.expires) - Date.now() <= 0) {
// you can dispatch inside store
this.store.dispatch(REFRESH_ACCESS_TOKEN);
}
// continue processing this action
return next(action);
}
const store = createStore(
... //add to your middleware list
applyMiddleware(refreshAuthToken)
);
Cons: Maybe you can need redux-thunk library if this middleware not solve your problem (its easy job. You can't say this even a con)
Pros: Best and safer way for your need and it will work everytime you call an action. It will works like a charm on SSR apps
3- or you can send store as parameter to your request method from component
Cons: Not the best way it will run after your components initialize and you can make easily mistake or can forget to add to your component.
Pros: At least you can do what you want
Maybe a better approach would be to store the token on the localStorage so that you don't need to access the Redux store to obtain it. Just fetch the token and store it this way:
localStorage.setItem('token', JSON.stringify(token))
Later in your service, just retrieve like this:
const token = JSON.parse(localStorage.getItem('token'));
If you don’t feel safe storing the token in the local storage, there's also the secure-ls package, that allows encryption for the data saved to the local storage.
I’m using next.js to build static HTML webpages.
One of my webpages needs data from a third-party API, which I’d like to fetch at build time and bake into the resulting HTML.
I don’t want this call to ever happen on the client, because:
CORS prevents the request from succeeding anyway
I would have to expose an API key on the client (no thank you)
I thought getInitialProps was the answer, because the fetched data is indeed baked in during the build/export process, but when I navigate away from the page and return from it, getInitialProps gets triggered on the client, breaking everything.
My current code in getInitialProps is something like:
static async getInitialProps(){
// Get Behance posts
const behanceEndpoint = `https://www.behance.net/v2/users/${process.env.BEHANCE_USERNAME}/projects?api_key=${process.env.BEHANCE_API_KEY}`
const behanceRes = await fetch(behanceEndpoint)
let behancePosts = await behanceRes.json()
// Return only the required number of posts
return {
behancePosts: behancePosts
}
}
Any advice or best practice on how to handle this? I know Gatsby.js does it out of the box.
one possibility would be, if you just want to execute this once on the server to check if the req parameter is present in getInitialProps: (Documentation)
req - HTTP request object (server only).
One dirty approach:
static async getInitialProps({ req }){
if (req) {
// only executed on server
// Get Behance posts
const behanceEndpoint = `https://www.behance.net/v2/users/${process.env.BEHANCE_USERNAME}/projects?api_key=${process.env.BEHANCE_API_KEY}`
const behanceRes = await fetch(behanceEndpoint)
let behancePosts = await behanceRes.json()
// Return only the required number of posts
return {
behancePosts: behancePosts
}
} else {
// client context
}
Hope this helps a little bit.
I have used CakePHP + AngularJS for the application
I have below code in Sevices file
test.factory('Dashboard', function ($http, $q) {
return {
userDirectory: function (){
return $http.get(hostName + 'dashboards/userDirectory.json');
}
}
});
The above code calls dashboards's controllers userDirectory function and return JSON data this is how it's work.
Some one raised one issue, When he hit url "http://hostname/dashboards/userDirectory.json" he can see the response data in browser and that is not authenticated. Is there any way through which I can secure it.
By any encoding/decoding or What you prefer.
Decode/encode doesn't make any sense here if the client can decode it the user can get the data as well.
Just send data the user is allowed to see and use, even if he is authorized for that request, remove everything else that is not needed.
Use a JWT token to authorize them
https://github.com/ADmad/cakephp-jwt-auth
http://www.bravo-kernel.com/2015/04/how-to-add-jwt-authentication-to-a-cakephp-3-rest-api/
http://florian-kraemer.net/2014/07/cakephp-and-token-based-auth-with-angular-js/