Angular: await for query string parameter but do something else if it doesn't appear - rxjs

I have tried to formulate a question title the most generic way that applies to my problem.
I have an Angular application where I have to handle authentication over an external requirement: either use a query string parameter token that has to be exchanged with the server for a JWT, or try to search for a JWT refresh token in the local storage.
It is:
First test the query string: if there is a queryString parameter token, grab the token, delete any JWT in the local storage, exchange the token via API for two JWTs (id_token and refresh_token)
Else go for the refresh token: if there is a refresh_token in the local storage available, exchange it for a JWT id_token via API
Else, if none of the two are available, the user is unauthenticated and a prompt should be displayed
I used Observables almost the correct way
this.queryParamMap$.unsubscribe();
this.queryParamMap$ = this.activatedRoute.queryParamMap
.subscribe(
params => {
let token = params.get('token');
........
if (!!token) {
doLoginWithToken();
else if (isJwtRefreshAvailable())
doLoginWithRefreshToken();
There is one problem with this approach: the very first time the application starts up, the query param map is empty, even if I follow a direct browser link http://localhost:4200?token=AAAAAAAA. I have to wait for the next element which contains the token.
This has two undesirable effects:
At the first attempt, being the token undefined, the application immediately tries to log in with the refresh token
If I filter the queryParamMap observable for a token being present, if a token is never present the observable will never emit, thus not activating the subscription.
My problem can be summarized/generalized as follows.
I have an Observable that I know for sure emits undefined the very first time, but either in no time it could be ready with a proper value, or it won't emit new values after the initial undefined.
Else said, while the observable emits undefined and my code starts reacting to it (e.g. by testing for token), a new value can be ready for emission right away.
How can I solve this in Rxjs? Note that accessing the JWT token from the local storage is a synchronous operation, but it's easy to create Observable.of(localStorage.get(KEY)) which emits immediately if a refresh token is present.
For that, I can't realistically use race operator, because the refresh token is always ready and always wins the race.
How can I write asynchronous code that performs like the steps I described earlier?
As the ultimate result of the authentication, an Observable<UserProfileDto | undefined> emits the information about the user, which is used to display personalized information.

You have 2 problems here:
On init you got "undefined" value (its because probably, under the hood, there is some BehaviourSubject emitting its default value). To overcome this, you can add RxJS operator (skip(1)) to skip that first value, but:
Problem here is if you do not have query values at all, you will not reach your subscribe function. Its because queryParamMap will emit a value only, if there is a change (and in that case there is not):
You can do it without Angular, to just parse url from:
const urlParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlParams.entries());
Or using router in Angular:
this.router.events
// Wait for the navigation end event (since component is initialized before angular router navigation is done)
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationStart) => {
// Take query params snaphot
const map = this.route.snapshot.queryParams;
});

Related

Epic EMR FHIR refresh_token FHIR.OAuth2.Authorize()

I am following example by https://www.patient.dev/2021/01/12/build-a-fhir-app-for-epic-ehr-systems/
Example works nicely in various variations.
I am trying to extend the example such that after first time user Authenticates/Authorizes, Epic would return back refresh token. My intent is that repeat Authorizations/Authentications would not be needed.
I modify Epic App at https://fhir.epic.com/Developer/Apps to request refresh_token, copy and save the provided client secret.
Next I call on launch.html:
FHIR.oauth2.authorize({
'client_id': 'xxxxxxxxxxxxxxmyclientidxxxxxxxx',
'client_secret': 'yyyyyyyymyclientsecret',
'scope': 'PATIENT.READ, PATIENT.SEARCH, OBSERVATION.READ, OBSERVATION.SEARCH',
'redirect_uri': 'https://myapp.myrepl.repl.co/app.html'
});
Observations:
There is no difference in behavior whether I add 'client_secret' or no.
Both cases login to Epic works, after login I land on page that asks me to do the normal authorizations plus define how long an access do I grant.
Succeeding redirect to .../app.html then leads to unsuccessful call :
FHIR.oauth2.ready().then(function(client){
myApp.smart = client
doRequests()
})
Question:
Is is wrong hypothesis to expect that Epic returns refresh_token to redirect uri page ?
If yes, should I modify call to FHIR.oauth2.authorize() ?
Or is it totally undoable with smart.js functions and I should write my own GET/POST calls for stepwise authorization & authentication?
Thank you for your support, Beibian

Jmeter token refreshing

I find the topic Refreshing Auth Token while keep the requests running in JMeter 3.3. I faced with same task - token refreshing each x time and other request sending at the same time. I've seen that author has find the solution. But I would be really appreciated if you could share the logic. I tried with global property in Jmeter, but it seems that I set one token for all users. The timer was allso added, but I'm getting one token for sessions. Probably we have a new solution for Jmeter 5.x. Thanks.
The solution would be the same, you just need to amend it a little bit and use __threadNum() function in order to create thread (virtual user)-specific properties.
Something like:
${__setProperty(token_${__threadNum},variable containing the token,)}
will generate the following JMeter properties:
token_0=token for the first virtual user
token_1=token for the second virtual user
etc.
which can be accessed similarly using __P() function like:
${__P(token_${__threadNum},)}
More information: Here’s What to Do to Combine Multiple JMeter Variables
Thank you so much! I have implemented my script using the following logic in one thread group:
Set var “Trigger” = 60 seconds (token refreshing interval, per minute);
Login user;
Get Token request; and execute actions:
Extract Token and save to vars;
Save “TokenTime” to vars (where “Login” from CSV is the name of property);
Calculate “TokenDiffTime” = “TimeNow- TokenTime” -> Save as property with unique name (the unique name can be the string “Login”+”TimeDiff”- where “Login” is dynamic Login from CSV and “TimeDiff” – just static string; the example of property name is “User1TimeDiff”).
Loop controller with logic:
If Controller (Is token Actual? -> check “TokenDiffTime” >=“Trigger”) -> Call Refresh Token request; Extract actual token.
Invoke App request; using actual token.
If Controller (Is token Actual? -> check “TokenDiffTime” >=“Trigger”) -> Call Refresh Token request; Extract actual token.
Invoke App request; using actual token.
So, such logic takes into account the response time delays and a new app request is invoking with actual token. “Trigger” is flexible value to set token refreshing interval. Hope such topic could be useful to someone.

How to get the result of a asynchronous action in NGXS?

I want to perform an action based to the result of an asynchronous NGXS action.
In a Angular frontend app I'm using NGXS for state management. Some of the actions involve talking to a backend via REST calls. Those actions are implemented as asynchronous actions, with the reducer functions in my state classes returning an Observable.
What I'm looking for is a way to get hands on the result of the backend call, to be able to perform some action.
One use case I'm trying to implement is navigation to just created objects: Business objects are created in the frontend (Angular) app with a couple of domain properties. They get persisted in the backend, and as a result an ID for this object is created and returned to the frontend, and incorporated into the NGXS store. As a direct response to this, I'd like to navigate to a detail view for the new object. To do so, I need
(a) the information that the call has been returned successful, and
(b) the answer from the backend (the ID in this case).
Another slightly more complicated use case is the assignment of a number of tags to an business object. The tags are entities by themselfes, and have an ID each. In the UI, the user can either pick existing or add new tags. Either way, multiple tags can be added in a single step in the UI, which means I have to
call the backend for each new tag to create the ID
after all missing tags are created, update the business object with the list of tag IDs
In general, there are use cases in the frontend that depend on the result of a backend call, and there is no clean way to find this result in the store (although it's in there)
I know I can subscribe to the Observable returned from the store's dispatch method (as shown in asynchronous actions).
I also know about action handlers. In both cases I can attach code to the event of an action finished, but neither option enables me to get the result of the backend call. In the fist case, the Observable carries the whole store, while in the latter case I get the original Action, which is unfortunately missing the essential information (the ID).
The part you're missing here are selectors. Dispatching actions is not supposed to give you back a result. The only purpose of the Observable returned by store.dispatch() is to tell you when the action's handlers are done.
To get to the data returned by your calls to the backend, you have to patch the state inside your action handler. And then, outside of your state, you can access the data using store.select() or store.selectSnapshot() depending on what you need. Your state class should look somewhat like this (untested):
#State()
export class SampleState {
#Selector(SampleState)
sampleSelector(state) {
return state.sampleObject;
}
#Action(SampleAction)
sampleAction(ctx: StateContext<any>, action: sampleAction) {
return sampleBackendCall(/* ... */).pipe(
tap((result) => {
ctx.patchState({ sampleObject: result });
})
);
}
}
Now you can access this result where ever you need using the Store. For the use case of navigating to an element after its creation, you can combine a subscription to store.dispatch() with a store.selectSnapshot() like this:
store.dispatch(new SampleAction()).subscribe(() => {
navigateTo(store.selectSnapshot(SampleState.sampleSelector));
});
Note that in this easy case a selectSnapshot is perfectly fine, as we only want to get the value we just finished writing into the state. In most cases though, you will want to use store.select() or the #Select() decorator because they return Observables which enable you to also correctly display changes in your state.
That said, I'd like to add that if saving data inside the state is not necessary for you at all, then probably NGXS is the wrong library for you in the first place and you could as well just use an ordinary angular service directly returning the result of the backend call, like suggested in the comments.

Pagination in Gmail API (Previous Token)

I have implemented GMail API which gets Emails for me. Now I am trying to add pagination to it I have succeed in getting next records but now I also want to have Previous option(which required previous token).
I am unable to get into it below is what I tried so far.
public function paginate(Request $request){
$label = $request->input("label");
$nextToken = $request->input("next");
$prevToken = $request->input("prev");
$messages = LaravelGmail::message();
$msg = $messages->take(3)->in($label)->all($nextToken);
$nextToken_New = $messages->pageToken;
return view('gmail.load_mails', ['messages' => $msg, 'nextPageToken' => $nextToken_New,
'prevPageToken' => $nextToken]);
}
Now In the above function nextPageToken is passed in view as $nextToken_New
and for prevPageToken I am unable to set previous page token.(In code I have set last nextPageToken to prevPageToken which is not working)
Remember prevPageToken will be used to set on back key.
The Gmail api does not support prevous page token. Its not going to return the value to you.
Your first option would be to save these tokens on your server and then when ever you want to go back a page simply supply the token you want to the page token field
The second option and the one that i personally feel would be the most logical. Would be to cache the data returned by these requests on your server so that
you dont have to make extra http calls to the server.
you are not eating addictal quota making a call you have already made before.
APIs were not meant for you to use to implement pagination in your application. You should only be requesting data once its your job to then cache that data so that you wont need to make the same request twice.

Ajax security: how to be sure that data sent back to the server is generated by my code?

I apologize in advance if this question sounds naive to you.
The problem is this: I have this function and I want the callback function to send the "response" back to my server via Ajax.
function FbInviteFriends()
{
FB.ui({
method: 'apprequests',
message: 'Hi! Join me on XXXXXXX'
},
//My callback function
function(response){
//Send response to my server
}
Is there a way to check that the response I'm going to receive server-side is actually the same I got when the callback function is called and that the response hasn't been modified on the client-side by the user?
Thanks!
There's a few ways, but all of them fall on the same principle - you can never know for sure, so treat it with a grain of salt and validate.
That said, one way to put at least one usage constraint may look like this:
Page accessed: Generate a token GUID. Render it at the client.
Store in the user session the moment it was created/used, together with user profile.
Client appends the token to all Ajax posts.
Token is validated at the server; must match SessionID, user profile (if any), and maximum usage timeout.
If it fails validation, abort the operation.

Resources