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
Related
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;
});
I'm having trouble validating a nonce created with wp_create_nonce() inside a hidden input with the name nonce in an html form:
<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('action_name'); ?>" />
The form submission is done via ajax and validated with check_ajax_referer('action_name','nonce'). This always returns -1. All REST endpoints have been tested without nonces and work 100% fine.
The issue seems to stem from wp's user identifcation.
My debugging so far
Nonce creation
Within wp-includes/pluggable.php wp_create_nonce('action_name') creates a nonce hashing various variables including the user id and the action.
Ajax call
I submit an ajax call which calls check_ajax_referer('action_name','nonce'). This in turn calls wp_verify_nonce($nonce,$action) which verifies the nonce by hashing the same variables and comparing the two.
Reverse engineering to locate problem
My problem is that wp_create_nonce('action_name') is being created with the correct user id. However, when I run check_ajax_referer('action_name','nonce') which calls wp_verify_nonce($nonce,$action) which in turn calls wp_get_current_user(); no user is found (user id is 0).
Evidence the problem is to do with user id
If I temporarily edit wp-includes/pluggable.php to force my user id, the nonce validation works fine. It's as if ajax requests to a known and valid endpoint are being treated as if the user is logged out regardless of whether they are or not.
I'm clearly missing something here, but I have no idea what.
This is happening because a separate nonce with the action wp_rest is not being sent by the server to the client and received back from the client in an HTTP request header called X-WP-Nonce with every REST request.
To get this working, you will have to generate a nonce like this:
wp_create_nonce('wp_rest')
...and provide it to the client making the rest call. Once your client has the nonce value, you need to add it to every REST request e.g.:
headers: {
'X-WP-Nonce': nonce,
}
Creating the nonce on the server and accessing it on the client can be done several ways. Using wp_localize_script() is the most common and probably best practice for WordPress. wp_localize_script() addds a global variable to the client for a script to access. See https://developer.wordpress.org/reference/functions/wp_localize_script/.
A couple of tutorials on oAuth use the Flask session to store state parameters and access tokens in the flask session. (Brendan McCollam's very useful presentation from Pycon is an example)
I understand that Flask stores the session in cookies on the client side and that they are fairly easy to expose (see Michael Grinberg's how-secure-is-the-flask-user-session). I tried this myself and was able to see the token the expiration, etc.
Is it correct to store the state and tokens in the flask session or they should be stored somewhere else?
Code example:
#app.route('/login', methods=['GET'])
def login():
provider = OAuth2Session(
client_id=CONFIG['client_id'],
scope=CONFIG['scope'],
redirect_uri=CONFIG['redirect_uri'])
url, state = provider.authorization_url(CONFIG['auth_url'])
session['oauth2_state'] = state
return redirect(url)
#app.route('/callback', methods=['GET'])
def callback():
provider = OAuth2Session(CONFIG['client_id'],
redirect_uri=CONFIG['redirect_uri'],
state=session['oauth2_state'])
token_response = provider.fetch_token(
token_url=CONFIG['token_url'],
client_secret=CONFIG['client_secret'],
authorization_response=request.url)
session['access_token'] = token_response['access_token']
session['access_token_expires'] = token_response['expires_at']
transfers = provider.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1')
return redirect(url_for('index'))
#app.route('/')
def index():
if 'access_token' not in session:
return redirect(url_for('login'))
transfers = requests.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1',
headers={'Authorization': 'Bearer ' + session['access_token']})
return render_template('index.html.jinja2',
transfers=transfers.json())
I think some tutorials over-simplify in order to show simpler code. A good rule of thumb is to use session cookies only for information that MUST be known by your application and your user's browser, and is not private. That normally translates into a Session ID and possibly other non sensitive information such as a language selection.
Applying that rule of thumb, I'd suggest the next to each of the tokens:
Authorization Token: this data is by definition known to both the user and the application, so it shouldn't be a security concern to expose it in the cookie. However, there really is no need to keep this token once you're given an access code, so I advice against keeping it locally or in your cookies.
Access Code: this data must be considered secret, and must only be known by your application and the provider. There is no reason to make it know to any other parties, including the user, therefore it should NOT be included in cookies. If you need to store it, keep it locally in your servers (perhaps in your database, referencing your users session ID).
CSRF State Token: this data is ideally included as a hidden form field and validated against a server side variable, so cookies seem like an unnecessary complication. But I wouldn't be concerned about this data being in a cookie, since it's part of the response anyways.
Keep in mind there are extensions such as flask-sessions, with which practically the same code uses server side variables instead of cookie variables.
So I looked at this post:
is an entity body allowed for an http delete request
Which seems to indicate that while it is 'ok' to do on some conceptual level, in practice it may not be doable because browsers just ignore it.
I have some express.js authentication middleware I need to get through, and I don't want to attach my user details to url params. All my other requests that need to authenticate attach these details to the body of the request.
Is there some way to force this? I saw some other posts where some people seemed to have success in passing a body with their delete request.
I am running a node/sails back-end. It always logs the body as undefined for a delete request. Is there any way to modify
The sails API pulls the id of the object to delete from the params, so we have to append the id to the url.
But if I want to pass some authentication details in a body for server-side verification before processing the delete request, I can't just stick them in an object as the second parameter of the delete request, like you can with $http.post.
Angular's post method automatically assigns whatever we insert as a second parameter to the body of the request, but the delete method does not.
Angular's $http.delete method does allow us to supply a config object as the second parameter, through which we can get access to the 'data' property. This is the same way post does it through it's second parameter.
So if we need to attach a body to a delete request we can use the following:
$http.delete('/api/' + objectToDelete.id, {data: {id: currentUser().id, level: currentUser().level}});
This will pass the object to delete's id in the url parameter, and my user credentials in the body as an object.
Honestly, everytime a trouble sounds like a "restriction of as REST", a rethink of the strategy and the philosophy might be a good idea.
I have some authentication middleware I need to get through
I don't want to attach my user details to url params
I'm not directly answering the question, but you should know that among the commons
URL parameters (or query, but URL anyway)
Body
there is a third option for "passing values to the server" :
request Headers
I'd just suggest to consider that third option to provide your credentials: request header.
Edit : following appendix would just apply to any "external" middleware, like a proxy server or whatever, not a true express middleware inside sails.js
In addition, that would be a good idea that your middleware stripped those headers before redirecting to the real action.
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.