Context:
I've implemented OpenIddict in my application, basing on GitHub readme. I currently use TokenEndpoint to log user in.
services.AddOpenIddict<ApplicationUser, UsersDbContext>().EnableTokenEndpoint("/api/Account/Login")
Although calling /api/Account/Info works and it returns token in response, I need to get some basic data about logged in user (username, email, account type). Response from token endpoint doesn't provide that.
I've found something like UserinfoEndpoint:
.EnableUserinfoEndpoint("/api/Account/Info")
But what I see after in http response is:
{
"sub": "ea2248b4-a[...]70757de60fd",
"iss": "http://localhost:59381/"
}
This should return me some Claims. As it doesn't return anything, I assume that no Identity Claims were created during token generation.
What I need to know in a nutshell:
Is using Token Endpoint correct way to log in user?
Do I need to generate Claims by myself?
Can I control Claims by myself and how?
How to make some Claims visible through UserInfoEndpoint?
Is using Token Endpoint correct way to log in user?
Yep.
Do I need to generate Claims by myself?
The userinfo endpoint simply exposes the tokens stored in the access token (which is something that may change in the future).
Can I control Claims by myself and how?
How to make some Claims visible through UserInfoEndpoint?
To allow the userinfo endpoint to return more claims, you'll have to request more scopes. Read http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims for more information.
In a future version, we may allow you to return custom claims, but it's not currently supported.
Related
I would like to use createCustomToken and signInWithCustomToken methods to implement user impersonation. Is it possible somehow to tell that current user is logged in with signInWithCustomToken to detect that currently user impersonation is taking place?
I guess I can write something to the local storage after signInWithCustomToken succeeded but maybe there is better way to do this?
When signing in with a custom token, the ID token will contain a claim specifying this. tokenObject.firebase.sign_in_provider will be equal custom.
When verifying server side, you can use the verifyIdToken and inspect this.
Firebase Auth specifies APIs to get this client side:
firebase.auth().currentUser.getIdTokenResult()
.then((result) => {
// Will output `custom`.
console.log(result.claims.firebase.sign_in_provider);
});
The best way I found for this is to set custom developer claims property when calling createCustomToken. On client-side I can parse id token and extract developer claims.
To add ADFS 3.0 authentication in our SPA we use the javascript sample and one (wsfed) external identityprovider, and we also add a local api for the SPA client
We also added a custom view to the login process, where the user could "Select WorkingContext" and we could set an additional claim.
Problem: How to add and retrive that additional claim?
Since we simply need the a few of the claim from ws federation, we've made a super simple callback where we just do the following (I'm answering the questions from the docs)
Handling the callback and signing in the user
inspect the identity returned by the external provider.
Yes, we get correct claims from wsfed
make a decision how you want to deal with that user. This might be different based on the fact if this is a new user or a returning user. New users might need additional steps and UI before they are allowed
in.
No additional steps required
probably create a new internal user account that is linked to the external provider.
No, we don't need the user, we just need the a few of the claims from wsfed, so we just return a TestUser based on the wsfed sub in FindUserFromExternalProvider
store the external claims that you want to keep.
Do we need to store the claims, will the claims not be embedded in the jwt token, and the token simply validated?
delete the temporary cookie
ok
sign-in the user
Here we would like to show a custom ui where the user should select a "workingcontext", and we could add the "workingcontext" as an additional claim.
Assuming the above is valid, how can we in step 6 add the extra claim?
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.ClientId));
doesn't seem to give any ways to add claims.
This is how we try to pass the additional claims through the login process:
var isuser = new IdentityServerUser(user.SubjectId)
{
DisplayName = user.Username,
IdentityProvider = provider,
AdditionalClaims = additionalLocalClaims
};
await HttpContext.SignInAsync(isuser, localSignInProps);
We just need to implement the IProfileService, that's all.
Question
How does User.Identity.GetUserId() finds the current user's Id?
Does it find the user Id from the Cookies, or does it query the database? Or any other methods?
Problem
For some reason, User.Identity.GetUserId() returns null when I add a valid Bearer Token to my Http request header and send the request to my controller's endpoint:
// MVC Controller Action Method
[Authorize]
public HttpResponseMessage(UserInfoViewModel model)
{
// Request passes the Authorization filter since a valid oauth token
// is attached to the request header.
string userId = User.Identity.GetUserId();
// However, userId is null!
// Other stuff...
}
How does User.Identity.GetUserId() finds the current user's Id?
ClaimTypes.NameIdentifier is the claim used by the function User.Identity.GetUserId()
You would need to add the claim in your authorization code,
identity.AddClaim(ClaimTypes.NameIdentifier, user.Id);
identity is of type ClaimIdentity.
When the user is logged into your app, the server, using ASP.NET Identity, validates your user using DB and creates a valid token that returns to the UI. This token will be valid to its expiration and has inside all information needed to authenticate and authorize the user, including the user's Id. Next calls from client side to server side must be done using this token in the http request header, but server will not call the DB again, because ASP.NET identity knows how to decrypt the token and get all the information of your user.
The use of cookies is only a way to store the token in the client side. As I commented above, you have to send the token on the next requests after the login, so you can store this token in cookies or in Session Storage in your browser.
First, make sure you're not allowing for non-authenticated users.
After that, you want to parse Bearer tokens you have to configure it.
You're going to the need this package Microsoft.Owin.Security.OAuth
And at startup if have to configure ASP.NET Identity to use Bearer Authentication with:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
// your options;
});
Probably on your StartupAuth.cs file
Am working on LTI Tool Provider, I have implemented an LTI auth package and am successfully able to get two (..one?) legged OAuth working aka match the signatures and we're all good to redirect, except one important thing.
What I want to be able to do, is
If this user doesnt exist, create it and log in
If the user exists, log our user in
Right now I have no way of actually determining my user once I redirect to a client route.
The LTI Consumer points to my Iron Router server route that looks something like:
Router.route('/lti', { where: 'server' }).post(function() {
provider.valid_request(request, function(error, valid) {
if (valid) {
this.response.writeHead(302, { Location: '/' });
} else {
this.response.writeHead(403);
}
});
return this.response.end();
});
Are there any packages I can use to get this working simply? Can I use something like accounts-base? Do I need to implement my own logic?
Any help or direction is appreciated.
Cheers.
I solved this by implementing a single-use authentication token system, handled by a custom login handler using accounts-base and the Accounts.registerLoginHandler method.
Rough auth flow overview:
LTI Route (server)
If authenticated, create a new account/update the old one
Insert a token + timestamp object into a collection.
Redirect to an authentication route, passing our token as a parameter
Auth route (client)
Check if our user is logged in. If so, redirect to our home route
If a token has been provided and it exists, mark it used. As we have no way of checking for a user in a server route, if a user has a session, closed and opens the link through the LMS again, we need to deal with our excess tokens.
If our user is not logged in, check for a token. If it exists, pass it to a custom authentication via Accounts.callLoginMethod
Our custom login handler validates our token. If legitimate, consume the token and log the user in.
My code is messy, but when I refactor I'll probably open source it as a Meteor package.
I've spend the last few days reading through all the specs with regards to OAuth2 and OpenIDConnect and implementing a test client using Thinktecture Identity Server. I've also followed several pluralsight courses and I think understand the main gist of it. However i'm still extremely confused about the Response Types.
OpenIDConnect spec specifies that the Hybrid flow response types are a combination of "code", "id_token" and "token". I understand the "id_token" allows us to get access to basic id information initially.
I also understand code" refers to the authorization code and "token" refers to an access token and combining "code" with one or both of the other two triggers the flow but my understanding was that you swap an authorization code for an access token in the Authorization flow, while the Implicit flow supplies the Access Code implicitly?
Could someone clear up my confusion?
The following statements that you made are correct:
code refers to the Authorization Code
token refers to an Access Token or (access_token)
in the Authorization Code flow one switches the code for an access_token
But part of your confusion may originate from terminology mixup:
the term Authorization flow is not entirely correct; its official name is Authorization Code flow
the term Access Code does not exist
the Implicit flow does not have an Authorization Code (nor Access code) in fact there's no credential (or grant) involved at all that allows the Client to get tokens from the Token endpoint, hence it's name
As #juanifioren pointed out, Hybrid flows combine things:
the code id_token flow would get a code and id_token in the Authentication Response directly but you'd use the code to get an access_token from the Token endpoint
the code token flow would get a code and access_token in the Authentication Response directly but you'd use the code to get an id_token and possibly another access_token in the backend from the Token endpoint
the code id_token token flow would get a code, access_token and an id_token in the Authentication Response directly and you could use the code in the backend to get another access_token from the Token endpoint
Getting an access_token from the Token endpoint differs from getting it from the Authorization endpoint because the confidential clients authenticate themselves to the Token endpoint (and not to the Authorization endpoint). Hence the access_token for the confidential part of the client might have more permissions and or a longer life.
See also a short thread on the spec mailing list on this topic: http://lists.openid.net/pipermail/openid-specs-ab/Week-of-Mon-20150209/005229.html
To understand the possible relationships between Response Types and Grant Types see IdentityServer4\Constants.cs
public static readonly Dictionary<string, string> ResponseTypeToGrantTypeMapping = new Dictionary<string, string>
{
{ OidcConstants.ResponseTypes.Code, GrantType.AuthorizationCode },
{ OidcConstants.ResponseTypes.Token, GrantType.Implicit },
{ OidcConstants.ResponseTypes.IdToken, GrantType.Implicit },
{ OidcConstants.ResponseTypes.IdTokenToken, GrantType.Implicit },
{ OidcConstants.ResponseTypes.CodeIdToken, GrantType.Hybrid },
{ OidcConstants.ResponseTypes.CodeToken, GrantType.Hybrid },
{ OidcConstants.ResponseTypes.CodeIdTokenToken, GrantType.Hybrid }
};
Your thoughts about Authorization Code Flow and Implicit Flow are right.
But I think you are over-complicating the hybrid flow. When using hybrid you just simply can get both code and id_token.
After that, either you can grab code and exchange it for access token or just use the id_token (or access token) directly. Both approaches have their own flaws, especially in terms of security.
https://medium.com/#darutk/diagrams-of-all-the-openid-connect-flows-6968e3990660#9401
6. response_type=code token
When the value of response_type is code token, an authorization code
and an access token are issued from the authorization endpoint, and an
access token is issued from the token endpoint. In addition, if openid
is included in the scope request parameter, an ID token is issued from
the token endpoint, too.
Both the authorization endpoint and the token endpoint issue an access
token, but the contents of the access tokens are not always the same.
Regarding this, “3.3.3.8. Access Token” in OpenID Connect Core 1.0
says as follows:
If an Access Token is returned from both the Authorization Endpoint
and from the Token Endpoint, which is the case for the response_type
values code token and code id_token token, their values MAY be the
same or they MAY be different. Note that different Access Tokens might
be returned be due to the different security characteristics of the
two endpoints and the lifetimes and the access to resources granted by
them might also be different.