What's the username parameter used for? - spotipy

I'm trying to authenticate with spotipy using Authorization Code Flow like this:
token = util.prompt_for_user_token(username, scope, client_id=client_id,
client_secret=client_secret,redirect_uri=redirect_url)
When I assign any string to "username", I'm asked to authenticate the request in a browser which is popping up; everything works fine.
When I set a different string to "username" before running my code a second time, the authentication is done against the previously authenticated username (which is still authenticated in the browser session); just as if the value of "username" is not taken into account at all.
Also, I seem to be able to set any arbitrary value to "username" like "pipapo"; when I login to my Spotify account (which isn't "pipapo", obviously) this one is authenticated and methods like current_user_saved_tracks() do get the resources of the account authenticated instead of "pipapo".
Anyways: The access_token and refresh_token are saved to the cachefile .cache-pipapo; thus saving the credentials of the "who-ever-logged-into-the-browser" to the file named after the "wrong" account.
So: What is this parameter good for then, if ultimately the user's interactive selections are responsible for what the code is doing? And why is this even a required parameter if more or less not utilized in the auth process?

I just had a look at spotipy/util.py myself, how def prompt_for_user_token(...) is designed and what parameter "username" is used for; indeed, it is used for defining the caching file name only to hand it over to oauth2.SpotifyOAuth() like so:
cache_path=".cache-" + username
So, you can use any value here; it does not need to be the correct username, necessarily.

Related

Implement one general Authorization Service which should be called when I put Authorize attribute on it in multiple applications/APIs

Has anyone an idear what to use as a general Authorization Service and have an working code example or good implementation steps how to implement such of thing.
It takes a lot of time to look what I am after, but didn't found any satisfied solution yet.
IdentityServer is not an option, while my permissions can not be stored as claims, because of the size of the token. It comes with about 200 persmissions, so it should be done in a dbcontext or something.
I looked at the PolicyServer, but it wasn't working as I expected. When I installed it at the IS4 application, it works on the IS4 controllers, but when the Authorize is called from an external application, it doesn't call the Authorize override at all were it should check the permissions.
And it seems that the permissions aren't set in the external application either in the User.Claims or what so ever. I'm missing some settings I think.
What I want to accomplish is that I have one permissions store (table) (which for example contains a bunch of index, add, edit or delete button or what so ever). The should be given to the autheniticated user which is logged in. But this single persmission-store should be available at all applications or APIs I run, so that the Authorize attribute can do his job.
I think it shouldn't be so hard to do, so I'm missing a good working example how to implement something like this and what is working.
Who can help me with this to get this done?
I wrote some code to get the permissions by API call and use that in the IsInRole override. But when I declare it with the Authorize attr, it will not get in the method:
[ApiController]
1) [Authorize]
public class AuthController : ControllerBase
{
private readonly IdentityContext _context;
public AuthController(IdentityContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
[HttpGet()]
[Route("api/auth/isinrole")]
public bool IsInRole(string role)
{
2) if (User.FindFirst("sub")?.Value != null)
{
var userID = Guid.Parse(User.FindFirst("sub")?.Value);
if([This is the code that checks if user has role])
return true;
}
return false;
This is the IsInRole override (ClaimsPrincipal.IsInRole override):
public override bool IsInRole(string role)
{
var httpClient = _httpClientFactory.CreateClient("AuthClient");
3) var accessToken = _httpContextAccessor.HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken).Result;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var request = new HttpRequestMessage(HttpMethod.Get, "/api/auth/isinrole/?id=" + role);
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
etc...
This isn't working while it is not sending the access_token in the request
The 'sub' isn't send
Is always null
The open source version of the PolicyServer is a local implementation. All it does is read the permissions from a store (in the sample a config file) and transform them into claims using middleware.
In order to use the permissions you'll have to add this middleware in all projects where you want to use the permissions.
Having local permissions, you can't have conflicts with other resources. E.g. being an admin in api1 doesn't mean you are admin in api2 as well.
But decentralized permissions may be hard to maintain. That's why you probably want a central server for permissions, where the store actually calls the policy server rather than read the permissions from a local config file.
For that you'll need to add a discriminator in order to distinguish between resources. I use scopes, because that's the one thing that both the client and the resource share.
It also keeps the response small, you only have to request the permissions for a certain scope instead of all permissions.
The alternative is to use IdentityServer as-is. But instead of JWT tokens use reference tokens.
The random string is a lot shorter, but requires the client and / or resource to request the permissions by sending the reference token to the IdentityServer. This may be close to how the PolicyServer works, but with less control on the response.
There is an alternative to your solution and that is to use a referense token instead of a JWT-token. A reference token is just an opaque identifier and when a client receives this token, he has go to and look up the real token and details via the backend. The reference token does not contain any information. Its just a lookup identifier that the client can use against IdentiyServer
By using this your tokens will be very small.
Using reference token is just one option available to you.
see the documentation about Reference Tokens

Cannot get Username / given_name when using angular-oauth2-oidc and Identity Server 4

I am following the Implicit Workflow example from the angular-oauth2-oidc documentation.
Everything works well in my Angular app, and I can login (during which I am redirected to Identity Server), get my token and use this token to access my Web Api.
However, I have noticed that the "given_name" claim is null, and therefore, the username is not displayed on the login page. Specifically, the following method from the sample code appears to return null:
public get name() {
let claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
return claims.given_name;
}
I thought perhaps this was a problem with permissions, but my scope is set to:
scope: 'openid profile email api1',
Any idea what I need to change to get this "given_name" claim?
For those who encountered the same issue. You can fix it by adding this line AlwaysIncludeuserClaimsInIdToken=true in the client settings on identity provider side.
OauthService.getIdentityClaims() is a Promise and holds UserInfo you can extract the name field with braces, so your function should be:
public get name() {
let claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
return claims['name'];
}
The answer marked as "Best answer" is not correct. Get the user claims in the 'idtoken' will cause that the 'idtoken' be very big and then you may exceed the size limit.
The correct implementation is to use the 'UserInfo' Endpoint and then use the method 'loadUserProfile':
Example:
getUserClaims() {
const user = this.oauthService.loadUserProfile();
console.log(user, user);
}
I had the same issue, in my case with an error displayed on the browser console, saying that Request was blocked by Security Policy.
even having the AllowAnyOrigin() method called in startup, I lacked to get the header allowed. So when in my angular aap i call the loadUserProfile method via the
token_received event, it sends some headers that were not allowed.
Finaly this fix my issue:
app.UseCors(options => options.AllowAnyOrigin().AllowAnyHeader());
Don't forget calling that before usemvc

Which IUser to Use in AcquireTokenSilentAsync in MSAL

I'm asking this question in the context of a mobile (Xamarin) app using Azure AD B2C.
The tl;dr; of this is: Should I always use the IUser obtained from the “sign-in / sign-up” policy when invoking PublicClientApplication.AcquireTokenSilentAsync?
Now let me explain a scenario a bit.
A user tries to use an app, but needs to do a password reset immediately.
Using the MSAL library, the PublicClientApplication.AcquireTokenAsync is called invoking the "reset password" policy.
Assuming a successful call, an IUser object is placed in the PublicClientApplication.Users collection.
In addition, the token returned in the AuthenticationResult from that function is enough to let the user access the resources that need the AD B2C token.
This then means that any future calls to PublicClientApplication.AcquireTokenSilentAsync would use the IUser which was associated with the "reset-password" policy.
It will work ... but is that a best practice?
Or should the app immediately prompt the user to log in, thus invoking the "sign-up/sign-in" policy & thus have the IUser associated with that policy added to the PublicClientApplication.Users collection. Then whenever the AcquireTokenSilentAsync is invoked, that particular IUser can be passed to it? (Instead of the one from the "reset password" policy.)
The same question can be asked whenever an "profile editing" policy is called. A new IUser is added to the collection, and assuming the user has already signed in previous, does it matter which IUser is sent to AcquireTokenSilentAsync?
Matt, I suggest you have a look at the following sample active-directory-b2c-xamarin-native, and it particular the GetUserByPolicy(IEnumerable<User> users, string policy) method located in UserDetailsClient/MainPage.xaml.cs#L76-L85. This method retrieves the right IUser to use given a policy.
foreach (var user in users)
{
string userIdentifier = Base64UrlDecode(user.Identifier.Split('.')[0]);
if (userIdentifier.EndsWith(policy.ToLower())) return user;
}
Its usage is also shown in the OnCallApi method in UserDetailsClient/MainPage.xaml.cs#L116

how does get_current_user work

I'm really confused how Google App Engine's User's get_current_user() works. I've looked around the internet at a bunch of different guides and tutorials about login and authentication, and many of them mention similar methods.
If there are a million users logged in to my application at the same time, how can that method possibly work? Does each user get their own instance of the server? How does the server know which client it is talking to?
It doesn't make sense to me at all.
When logging in (by clicking on the URL generated by create_login_url()) a cookie containing user identifying information is prepared and pushed on the client side, then used in subsequent requests until the user logs out or the cookie expires. Calling get_current_user() simply checks the cookie existance/information and responds accordingly.
On the development server the cookie is named dev_appserver_login. I can no longer check the cookie name on GAE as I switched away from the Users API.
The actual handling of the cookie seems to happen somewhere on the Users service backend, for example, by looking at the google/appengine/api/users.py file in the python SDK:
def create_login_url(dest_url=None, _auth_domain=None,
federated_identity=None):
...
req = user_service_pb.CreateLoginURLRequest()
resp = user_service_pb.CreateLoginURLResponse()
try:
apiproxy_stub_map.MakeSyncCall('user', 'CreateLoginURL', req, resp)
...
The end point (at least for the development server) seems to somehow land somewhere in google/appengine/tools/appengine_rpc.py, for example:
#staticmethod
def _CreateDevAppServerCookieData(email, admin):
"""Creates cookie payload data.
Args:
email: The user's email address.
admin: True if the user is an admin; False otherwise.
Returns:
String containing the cookie payload.
"""
if email:
user_id_digest = hashlib.md5(email.lower()).digest()
user_id = "1" + "".join(["%02d" % ord(x) for x in user_id_digest])[:20]
else:
user_id = ""
return "%s:%s:%s" % (email, bool(admin), user_id)
def _DevAppServerAuthenticate(self):
"""Authenticates the user on the dev_appserver."""
credentials = self.auth_function()
value = self._CreateDevAppServerCookieData(credentials[0], True)
self.extra_headers["Cookie"] = ('dev_appserver_login="%s"; Path=/;' % value)

is FB's db (Oracle?) of "available username" in 'real-time' or gets updated dayly/wekly or never?

Scenario:
there is FB user with Facebook (FB) ID and personal profile.
that FB ID also has one Page.
Now that ID wants to assign an available username "Myusername" to its Page (not assigning that available "Myusername" to ID itself).
Error occurred and available "MyUsername" got assigned to ID .
In order to correct that, and to release "MyUsername" to the availability status,
ID's username "MyUsername" is changed to "NewReplacingUseername" .
However, when then trying to assign "MyUsername" (expected to be now AGAIN availabe) to the ID's Page, FB returns "Username not availabe".
However,
https://graph.facebook.com/MyUsername
returns
{
"error": {
"message": "(#803) Some of the aliases you requested do not exist: MyUsername",
"type": "OAuthException"
}
}
which should mean that "MyUsername" IS available !
Any help ?
========================
#OffBySome
thanks for pointing to that FAQ.
But that FAQ use terminology
"securing" a username
and
"diffrent account".
But this is NOT "different" account.
Page belong to the same ID account to which username was initially (arroneously) given but later that username to ID itself was replaced with NewUserName in order to free original Username to be given to the Page of same account to which ID belongs to.
As for "securing" , it is not anymore "secured" as it was replaced with NewUsername.
I need tech confirmation: Does FB store FOREVER any 'username' ever typed, even in error,
even if an account, who typed it in the first place, REPLACED it with something different, i.e. it is not used anymore / it is abandon ?
And, once again, this is within SAME account !
You cannot transfer a user name. Once it has been assigned it cannot be re-assigned so that is why you are getting that error message. This is documented on their FAQ:
Can I transfer my username to another account? Once you have secured a
username, it is not possible to transfer it to a different account on
Facebook. Also, when an account is removed from the site, its username
will be unavailable. Facebook does this for security reasons, namely
to prevent username squatting.

Resources