How to access "current logged-in user" in remote methods? - node.js

recently in one of my applications I needed to access currently logged-in user data for saving in another model (something like the author of a book or owner of a book). in my googling, I encountered these references but none of them was useful.
https://github.com/strongloop/loopback/issues/1495
https://docs.strongloop.com/display/public/LB/Using+current+context
...
all of them have this problem about accessing context or req object. after three days I decided to switch to afterRemote remote hook and add Owner or Author on that stage.
but something was wrong with this solution.
in strongloop's documentations (https://docs.strongloop.com/display/public/LB/Remote+hooks) there is a variable as ctx.req.accessToken that saves current logged-in user access token. but in the application this variable is undefined.
instead, I found accessToken in ctx.req.query.access_token and it was currently access_token variable that is sent to the server.
here is my problem:
is this variable (ctx.req.query.access_token) always available or
it's just because loopback-explorer send access_token as GET
variable?
in production mode do applications need to send access_token as
GET variable or it should be sent as Authorization in the header?
why ctx.req.accessToken is undefined?
could these things change over time? cause most of users encounter this problem due to deprecation of app.getCurrentContext()

Is this variable (ctx.req.query.access_token) always available or
it's just because loopback-explorer send access_token as GET
variable?
Well if your application always sends in the querystring, then it'll be always available for you, but it also sent in the header, or cookie or in the request body, but I don't suggest using it because it if the user logged in and the access token is valid and ctx.req.accessToken should be available and you can use it.
In production mode do applications need to send access_token as
GET variable or it should be sent as Authorization in the header?
I believe Authorization header is preferred, as if you send it in a GET variable, well it'll be visible in the logs and someone with the access to the logs can access the session(well unless you trust everyone), other than this it's fine to have it in a GET variable. Though I believe loopback client SDKs(Angular, Android, iOS) all send it via Authorization header by default, so you might have to configure them(maybe not possible).
Why ctx.req.accessToken is undefined?
Sometimes the context is lost thanks to the database drivers connection pooling, or the context req is lost(ctx.req) and they are null.
Assuming ctx.req is defined(because sometimes it's not), then probably that means the user is not logged it, or it's access token wasn't valid(expired or not in database). Also it could be a bug(maybe misconfiguration on your side), which also means for you that you will authentication problems.
Could these things change over time? cause most of users encounter this problem due to deprecation of app.getCurrentContext()
app.getCurrentContext is risky to use and I don't suggest unless you have no other solution. If you use it and it works, it might stop working if the database driver changes or in some corner cases that you haven't tested it, it might not work.

In the updated doc https://loopback.io/doc/en/lb3/Using-current-context.html
add this in your remoting metadata
"accepts": [
{"arg": "options", "type": "object", "http": "optionsFromRequest"}
]
then
MyModel.methodName = function(options) {
const token = options && options.accessToken;
const userId = token.userId
}
but it says
In LoopBack 2.x, this feature is disabled by default for compatibility reasons. To enable, add "injectOptionsFromRemoteContext": true to your model JSON file.
so add "injectOptionsFromRemoteContext": true on your model.json file

Related

Prevent showing the UI5 app internal page without successful authentication

OpenUI5 version: 1.86
Browser/version (+device/version): Chrome Dev
Upon the authentication I validate the user session:
if (isUserSessionValid) {
const oRouter = UIComponent.getRouterFor(this);
oRouter.navTo("overview");
} else {
this.getOwnerComponent().openAuthDialog();
}
If isUserSessionValid is true, then I forward an user to the internal page, otherwise I show the login dialog.
The problem is, however, that an user can change the value of isUserSessionValid in DevTools and then getting forwarded to the UI5 app internal page. Of course, due to a lack of a valid session, no piece of the business data will be displayed, just an empty UI5 app template, but I would like to prevent even such screen.
If it would be a classical webapp, I would just send an appropriate server response with a redirect to the login page (e.g. res.redirect(403, "/login");). But, if I understand it correctly, since I'm sending am asynchronous request, a plain res.redirect won't work out and I'm required to implement a redirection logic on the UI5-client, which can be manipulated and bypassed by user.
How to prevent a manipulation of a view navigation in UI5 and ensure that unauthorized user can't get any piece of the UI5-app code?
The answer from SAP:
If you want to prevent an unauthorized user from accessing the client-side code (e.g. view/controller) you need to enforce
authorization on the server also for those static files. When bundling
the application code you also need to ensure that those files are
separate from the "public" files. One approach would be to have 2
separate components, one for the public page/auth dialog and one for
the actual application.

How can I protect the loopback explorer by username and password?

I've just started using loopback4 and I would like to protect the /explorer from being public. The user would initially see a page where username and password must be entered. If successful, the user is redirected to /explorer where he can see all API methods (and execute them). If user is not authenticated, accessing the path /explorer would give a response of "Unauthorized". Is there a way to easily implement this?
There is issue talking about a GLOBAL default strategy is enabled for all routes including explorer in https://github.com/strongloop/loopback-next/issues/5758
The way is to specify a global metadata through the options:
this.configure(AuthenticationBindings.COMPONENT).to({
defaultMetadata: {
strategy: 'JWTStrategy'
}
})
this.component(AuthenticationComponent);
registerAuthenticationStrategy(this, JWTAuthenticationStrategy)
But in terms of enabling a single endpoint added by route.get(), it's not supported yet, see code of how explorer is registered. #loopback/authentication retrieves auth strategy name from a controller class or its members, but if the route is not defined in the controller, it can only fall back to the default options, see implementation

Spotify API Token Scope Issue

I have been at this for sometime now and wanted to see if anyone had and idea of what I could be doing wrong. What I am trying to do is add a song to a playlist using the provided Spotify Web APIs. According to the documentation on this https://developer.spotify.com/documentation/web-api/reference/playlists/add-tracks-to-playlist/ I need to establish the scope of the user.
"adding tracks to the current user’s private playlist (including collaborative playlists) requires the playlist-modify-private scope" I have created the playlist as collaborative and I am using the login credentials of my personal account to reach this playlist I created. all this is under the same login.
What I am finding is that my scope is not getting added to my token on my call for my token causes a 403 error when I try to add the song.
Here is what that call looks like
https://accounts.spotify.com/authorize/?client_id=mynumber&response_type=code&scope=playlist-modify-private&redirect_uri=http:%2F%2Flocalhost:55141/Home/GetToken/
here are the docs on using authorization to get the correct token.
https://accounts.spotify.com/authorize/?client_id=894400c20b884591a05a8f2432cca4f0&response_type=code&scope=playlist-modify-private&redirect_uri=http:%2F%2Flocalhost:55141/Home/GetToken/
further more if I go into the dev support here
https://developer.spotify.com/documentation/web-api/reference/playlists/add-tracks-to-playlist/
and click the green try button and then request a new token it works.
Bottom line some how my request is not taking my scope request. Any Ideas?
Thanks
To get the token with a specific scope you need to go to the authorize endpoint and get the code. The code is what you want to get to be able http post to the endpoint https://accounts.spotify.com/api/token and get a token with your desired scopes. You can simply get the code by pasting a url like this in your browser...
https://accounts.spotify.com/authorize?client_id=<client_id>&response_type=code&scope=streaming%20user-read-email%20user-read-private&redirect_uri=<redirect_uri>
Only add %20 in between scopes if you have multiple ones
You will then be sent to spotify's website and they'll verify you want to do this. Once you verify it your browser will redirect you to what you set the redirect_uri to be in the url above. At the end of the url that you are sent to, you should be able to see the parameter name code with the code value assigned to it. You then get that code and put it in your http post body params to the https://accounts.spotify.com/api/token endpoint. Make sure you accurately follow the query params requirements in your post method.
An example of the post in python using the requests library:
authorization = requests.post(
"https://accounts.spotify.com/api/token",
auth=(client_id, client_secret),
data={
"grant_type": "authorization_code",
"code": <code>,
"redirect_uri": <redirect_uri>
},
)
authorization_JSON = authorization.json()
return authorization_JSON["access_token"]
In the end you should get a json that shows the scopes you set a long with a refresh the token later on to make more requests.
I know this answer is quite late but I was experiencing the same issue as well which is how I came across this question. I hope this helps anyone that sees this at a later date.
Source: https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow

ServiceStack CredentialsAuthProvidercheck if authenticated

Is there an easy way I can easily check whether I am currently logged into ServiceStack Auth by using a REST endpoint?
ServiceStack v4 has been recently updated, Friday 16 May, (in this commit) to support returning session information by making a GET request to the Authenticate route /auth.
Thus if you have a valid session you will get a response such as:
{
"UserId":"1",
"SessionId":"1",
"UserName":"bob",
"ResponseStatus":{}
}
Otherwise you can always create a service that returns information about your session yourself. See my other answer for an example of this method.
Hope that helps.

Occasionally disabling Pyramid middleware

Note: If it's any help, I'm using Pyramid 1.3.2. I know it's a little out of date, I would prefer not to update right away, but I might be able to force an update if the latest version provides better support for this use case.
The Pyramid-based application I'm working on has a strict authorization policy: all calls must be authenticated. Since 1) it's tedious to add this manually on every request handelr; and 2) we don't want anybody to "forget" adding authentication, we enforce this server-wide using a simple Pyramid middleware (tween) that verifies all incoming requests.
Recently, this restriction has been slightly relaxed: occasionally, some resources should support (safe & idempotent) GET without authentication.
It seems this is directly opposed to the usual design ideas behind authentication in most web frameworks (optional authentication), so I can't get it to work quite as expected.
QUESTION: What is the correct approach to implementing an authorization middleware that authenticates & verifies authorization by default, but can be disabled on a view-by-view basis?
So far, I've tried adding a simple decorator like so:
def allows_anonymous_access(f):
f.allows_anonymous_access = True; return f
#allows_anonymous_access
def my_pyramid_view(request):
# ...
In my middleware, I would like to use it like this:
def authorization_middleware(handler, registry):
def verify_authorization(request):
# Identify the user making the request. Make sure we get the
# user's identify if provided, even when the request handler
# allows anonymous access.
try:
request.principal = extract_user(request)
except InvalidCredentials, error:
if getattr(handler, 'allows_anonymous_access', False):
request.principal = AnonymousUser()
else:
raise HTTPUnauthorized(...)
# Invoke the handler.
return handler(request)
# Middleware that will pre/post-process the request.
return authorization_middleware
However, when the middleware executes, handler is not my view. It happens to be a bound method (pyramid.router.Router.handle_request) which does not provide me access to the view callable, meaning I cannot access the flag set by the middleware.
You probably want pyramid.config.set_default_permission(permission). From docs:
Adding a default permission makes it unnecessary to protect each view
configuration with an explicit permission, unless your application
policy requires some exception for a particular view.
If a default permission is in effect, view configurations meant to
create a truly anonymously accessible view (even exception view views)
must use the value of the permission importable as
pyramid.security.NO_PERMISSION_REQUIRED. When this string is used as
the permission for a view configuration, the default permission is
ignored, and the view is registered, making it available to all
callers regardless of their credentials.
Answer provided by raydeo_ on #pyramid freenode IRC channel.

Resources