I have implemented the authentication with OWIN and bearer token and it works fine when user tries to login. So I go to the special \Token url and provide username/password and get this token in response.
But I have a register user functionality as well and ideally I would like to get this token right away after I do register a user. I can go to the \Token internally in a web server, or do \Token after registration on the client, but I hope there is a solution where I can do something like
public Token RegisterUser(RegisterUserRequest request)
{
// Registration here
return OAuthProvider.GenerateToken(username, password)
}
Is it possible to do something like that?
This took ages for me to figure out so I thought I'd add it here.
You can manually generate a token using the Protect function on AccessTokenFormat of your OAuthAuthorizationServerOptions object, assuming you've made it static in your startup class as otherwise it will be null.
AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, new AuthenticationProperties());
string token = Startup.OAuthServerOptions.AccessTokenFormat.Protect(ticket);
Seems a very odd place to put the token generation logic.
Anyway you can then do with the token as you wish, likely attach it to your outgoing header or response object.
I was looking for the same thing you were, but I couldn't find anything, so I just made the request internally. Not very much work, but still a little disappointing.
public JToken GetLoginResponse(UserSignupDto dto)
{
var tokenPath = Request.RequestUri.GetLeftPart(UriPartial.Authority) + "/token";
var reqData = string.Format("grant_type=password&username={0}&password={1}&client_id={2}", dto.Email, dto.Password, dto.ClientId);
using(var client = new WebClient())
{
return JObject.Parse(client.UploadString(tokenPath, reqData));
}
}
Related
I have a web application with a number of modules. One of the modules grabs a number of excel files from SharePoint directories, and then combines the data in them. So far, I have been just mapping the folders to OneDrive and accessing them that way. But this always uses my OneDrive credentials, which need to be refreshed from time to time. The right way to do this is to access them directly from Sharepoint on behalf of the user logged into my web application. I have the delegated API permission things set up in Azure, and I have the client ID and secret, etc.. I've been reading a number of articles on how to do this. All of them talk about how to get the token on behalf of someone else. These articles also talk about the assertion token needing to be passed in order to get the on behalf of token. However, they don't tell you how to get the assertion token in the first place. Here is the code I currently have:
'''var client = new RestClient("https://login.microsoftonline.com/XXXX/oauth2/v2.0/token");
var request = new RestRequest();
request.Method = Method.Post;
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("client_id", "MYCLIENTID", ParameterType.GetOrPost);
request.AddParameter("client_secret", "MYSECRET", ParameterType.GetOrPost);
request.AddParameter("scope", "https://MYTenent.sharepoint.com/.default", ParameterType.GetOrPost);
request.AddParameter("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer", ParameterType.GetOrPost);
request.AddParameter("requested_token_use", "on_behalf_of", ParameterType.GetOrPost);
RestResponse response = client.Execute(request);'''
The result of this is of course an error that the assertion was not supplied. I didn't supply any more code, because I can't even get passed this. The rest of my code takes the token and passes it to an auth provider, which is then used to instantiate the GraphServiceClient. Based on what I've read, that client is then used to get the lists, files, etc...
So, my question is, how do I get the assertion token in the first place? I'm hoping the code I have written so far is in the correct direction and all I'm missing is the assertion token.
UPDATE:
I've gotten one answer that really didn't help. I pretty much copied and pasted the code (replacing the clientID, etc..) and I received an error> I was going to copy and paste it from the solution comments provided in the answer, but I guess you can't do that while editing.
Someone also asked if I was able to get the auth code from the URL. The answer to that is no. We use 2 factor authentication, and I tried to manually look at the URLS as I was logging in, while using break points to slow things down a bit. And I did not see an auth code. I did put a break point directly after the line of code:
var info = await _signInManager.GetExternalLoginInfoAsync();
And when I look at the info variable, I can see 4 tokens. One of them is an access token and another is an ID token. The last one is an expiration date. I don't see an auth code, and from what I understand, by the time I see the access code, it's too late. The auth code was already used to get the access code.
UPDATE 2:
I know that OBO is not what I want. I also know that in order to use delegated permissions, I need to use the Auth Code flow and not client credentials. I can't seem to get the auth code from the users initial log in. And I don't know how to get it otherwise.
For those of you that might be thinking "Does he need to be spoon fed?", the answer is yes, I do. I need a simple code example that will get me the auth code, so I can use it in the rest of the code I already have. If anyone can paste that code into an answer and not provide a link, that would be great. I'm sorry, but the links I have been given, just go to microsoft learn sites that go through the explaination, but don't give any code samples.
The OBO flow is obviously not applicable in your context, and if you're going to get an access token on behalf of a logged in user, then you should focus on auth code flow or ROPC flow.
The corresponding C# code segment is:
using Microsoft.Graph;
using Azure.Identity;
var scopes = new[] { "https://{tenant-name}.sharepoint.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "tenant id";
// Values from app registration
var clientId = "client id";
var clientSecret = "client secret";
// For authorization code flow, the user signs into the Microsoft
// identity platform, and the browser is redirected back to your app
// with an authorization code in the query parameters
var authorizationCode = "authorization code ";
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// https://learn.microsoft.com/dotnet/api/azure.identity.authorizationcodecredential
var authCodeCredential = new AuthorizationCodeCredential(
tenantId, clientId, clientSecret, authorizationCode, options);
var accessToken = await authCodeCredential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes) { });
Console.WriteLine(accessToken.Token);
//var graphClient = new GraphServiceClient(authCodeCredential, scopes);
I have a .NET6 ASP.NET Core application that implements a gRPC service as a server.
Authentication is done using Bearer tokens (JWT). After successful authentication+authorization, a server method is invoked to deal with a request, like so:
[Authorize("MyPolicy", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public override Task<MyResp> MyCall(MyReq request, ServerCallContext context)
{
// ok, I come here
}
Now I need to access one of the claim values that I can expect to be in the token. And I can access the token and find the value I need like so:
var s = context.RequestHeaders.Single(m => m.Key == "authorization").Value;
if (s.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
s = s.Remove(0, 7);
var t = new JwtSecurityTokenHandler().ReadJwtToken(s.Trim());
// now I can look into t.Claims
Fine.
But is there a better way? Before my handler becomes activated, something in the middleware must have inspected that token anyway. Is the collection of claims already available somewhere? I would expect it to be in the ServerCallContext, but I can't find it.
I generated an API client with AutoRest and am using the --add-credentials parameter so that I can pass in a bearer token. In order to get the token, I need to be able to instantiate the object and call my login method like this:
var client = new IOIWebAPI(new Uri("https://localhost:44325", UriKind.Absolute));
var loginResult = client.Login(authModel);
The problem is that every constructor requires ServiceClientCredentials. From what I understand, I need to create an instance of TokenCredentials, which includes the token string. But I can't do that because I can't get the token string without calling Login. And I can't call Login without having the token string.
I'm sure I'm just misunderstanding how to consume the API client. But any ideas on what I'm doing wrong here?
My best guess is that --add-credentials does not support unauthenticated endpoints. When you add creds, AutoRest assumes everything needs auth. I submitted an issue for this, but I suspect it won't be addressed any time soon.
My workaround was to create a TokenHelper class. I copy and pasted the code that AutoRest generated from my login endpoint into that class. So the code stays consistent, but it's not ideal because I may forget to update the endpoint if it ever changes.
var tokenHelper = new TokenHelper(baseUri);
var tokenResult = tokenHelper.GetTokenAsync(authModel).GetAwaiter().GetResult();
var token = new TokenCredentials(tokenResult.AccessToken, "Bearer");
var client = new IOIWebAPI(baseUri, token);
I'm implementing a straight out of the box solution using IDserver4(2.3) targeting .netcore 2.2 that communicates with a FHIR client by calling:
Url/api/openid/openapp?launch=12345t6u-34o4-2r2y-0646-gj6g123456t5&iss=myservice&launchOrganization=tilt
with some HL7 simulated scopes etc. The flow is okay all the way to the token endpoint serving access and id tokens using the quickstart on an IIS with certificates and all the bezels.
My problem lies in that the client requires a parameter to be passed to the external client pointing to a file or something on the server where I have some test patient data stored/or served as Json.
Any competent way to pass a parameter with the body or the header for example? And do you do it at the authorization or the authentication, or along with the tokens? Lets call it context. The service shut me down when i reach it. Says this on their side 'TypeError: Parameter "url" must be a string, not undefined'
Thanks in advance.
Got it using:
public class CustomClaimInjection : ICustomTokenRequestValidator
{
private readonly HttpContext _httpContext;
public CustomClaimInjection(IHttpContextAccessor contextAccessor)
{
_httpContext = contextAccessor.HttpContext;
}
public Task ValidateAsync(CustomTokenRequestValidationContext context)
{
var client = context.Result.ValidatedRequest.Client;
//client.Claims.Add(new Claim("sub", sub)); // this will be [client_sub]
context.Result.CustomResponse = new Dictionary<string, object>
{
{"example-launchcontext", "https://url/" }
};
return Task.CompletedTask;
//return Task.FromResult(0);
}
}
I think I understand your problem now, and I think you would like a successful authentication to return additional information about where the patient's file is stored. I would store this in the token as a claim since it can be expressed as a statement about the subject (the user). This can be done in the registered (through dependency injection) implementation of the IProfileService. In the implementation of 'GetProfileDataAsync' you can set the issued claims using the 'ProfileDataRequestContext' parameter's property 'IssuedClaims'. These claims will be used to populate the id token which is what you should be looking to do.
I'm trying to build a system working with ADFS and claims. At the moment, this is just a "toy" implementation.
I've built a very simple MVC web application, set it up using the "Identity and Access..." wizard in Visual Studio to talk to an ADFS 2.0 server, and deployed it to an IIS server. All works fine, and I can examine and list the received claims.
The next step is to build a Web API based REST service (representing back-end services that the MVC application is going to depend on), so I want to pass the credentials across to that back-end server so that it can make suitable authorization decisions.
So the first step is for me to create the delegation token (and I'll then, hopefully, work out what to do with it in terms of the HttpClient class to make the rest call). I've got this:
//We need to take the bootstrap token and create an appropriate ActAs token
var rst = new RequestSecurityToken
{
AppliesTo = new EndpointReference("https://other-iis.example.com/Rest"),
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Symmetric,
ActAs = new SecurityTokenElement(((BootstrapContext)((ClaimsIdentity)User.Identity).BootstrapContext).SecurityToken)
};
var sts = new SecurityTokenService(); //This line isn't valid
var resp = sts.Issue(System.Threading.Thread.CurrentPrincipal as ClaimsPrincipal, rst);
But, the issue is that SecurityTokenService is abstract. I can't find any types derived from this class in either System.IdentityModel nor System.IdentityModel.Services, and the above doesn't include any reference to the ADFS server which I'll obviously need to provide at some point.
Of course, I may be going down completely the wrong route also, or am just hitting a minor stumbling block and not seeing a much larger one looming in the distance, so any advice on that would be appreciated also.
I've looked at, for example, Identity Delegation Scenario, but that uses CreateChannelActingAs, which I don't think is going to work when I'm talking to a rest service (or will it?), and also doesn't seem to apply to .NET 4.5.
I am requesting tokens from an ADFS 2.0 for caching and looking at the DisplayToken. Maybe this can help you get started.
Here is what I can up with:
public SecurityToken GetToken(out RequestSecurityTokenResponse rstr)
{
Console.WriteLine("Connecting to STS...");
WSTrustChannelFactory factory = null;
try
{
if (_useCredentials)
{
// use a UserName Trust Binding for username authentication
factory =
new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
"https://<adfs>/adfs/services/trust/13/UsernameMixed");
factory.TrustVersion = TrustVersion.WSTrust13;
// Username and Password here...
factory.Credentials.UserName.UserName = "username";
factory.Credentials.UserName.Password = "password";
}
else
{
// Windows authentication over transport security
factory = new WSTrustChannelFactory(
new WindowsWSTrustBinding(SecurityMode.Transport),
"https://<adfs>/adfs/services/trust/13/windowstransport") { TrustVersion = TrustVersion.WSTrust13 };
}
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = SvcEndpoint,
KeyType = KeyTypes.Symmetric,
RequestDisplayToken = true
};
Console.WriteLine("Creating channel for STS...");
IWSTrustChannelContract channel = factory.CreateChannel();
Console.WriteLine("Requesting token from " + StsEndpoint.Uri);
SecurityToken token = channel.Issue(rst, out rstr);
Console.WriteLine("Received token from " + StsEndpoint.Uri);
return token;
}
finally
{
if (factory != null)
{
try
{
factory.Close();
}
catch (CommunicationObjectFaultedException)
{
factory.Abort();
}
}
}
}
You might have to acivate the UsernameMixed Endpoint in your ADFS 2.0 if you want to use it and don't forget to restart the service afterwards!
From msdn
To create an STS you must derive from the SecurityTokenService class. In your custom class you must, at a minimum, override the GetScope and GetOutputClaimsIdentity methods.
Not sure how much this will help you, but You're not supposed to create a SecurityTokenService. You are not creating a new token here, and your aplication is not supposed to act as the STS - this is what the AD FS is for.
Your application should only delegate the token received from the AD FS to the service (the concept is described in the link from msdn you provided in your question)
Im guessing theres a good chance the web api will suppor this as well, as its built upon wcf, and from the http point of view - theres no reason it wont support a ws-federation/saml 2 tokens.
EDIT:
This video (starting at 35:00+-) shows a way, i think, to implement what youre looking for, with ws-federation saml token. im guessing its also possible with a saml2 token