I need to set the credentials scope to access some APIs
my code is like that
ServiceAccountCredential saCredential;
using (var fs = new FileStream(#"C:\work\ConsoleApp2\google_credentials\foo.json", FileMode.Open, FileAccess.Read))
{
saCredential = ServiceAccountCredential.FromServiceAccountData(fs);
}
//saCredential.Scopes = new List<string> { "" };
var accessToken = saCredential.GetAccessTokenForRequestAsync().Result;
how can I set the scopes? now I get
TokenResponseException: Error:"invalid_scope", Description:"Invalid OAuth scope or ID token audience provided.",
the Scopes property is read-only
I succeeded to do it like that
GoogleCredential credential = GoogleCredential.GetApplicationDefault();
credential = credential.CreateScoped(new List<string>() { "https://www.googleapis.com/auth/compute" });
var dataflowService = new DataflowService(new Google.Apis.Services.BaseClientService.Initializer()
{
HttpClientInitializer = credential
});
Related
Google.GoogleApiException: 'The service people has thrown an exception. HttpStatusCode is Forbidden. Request had insufficient authentication scopes.'
When invoking following code in C#
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = ClientId,
ClientSecret = ClientSecret
},
new[] { "profile", "https://www.googleapis.com/auth/contacts" }, "me", CancellationToken.None).Result;
var service = new PeopleServiceService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyApp",
});
PeopleResource.ConnectionsResource.ListRequest peopleRequest =
service.People.Connections.List("people/me");
peopleRequest.PersonFields = "names,emailAddresses";
peopleRequest.SortOrder = (PeopleResource.ConnectionsResource.ListRequest.SortOrderEnum)1;
ListConnectionsResponse people = peopleRequest.Execute();
List<string> contacts = new List<string>();
foreach (var person in people.Connections)
{
contacts.Add(person.Names[0].DisplayName);
}
Please let me know how to fix the issue.
Receiving an unauthorized response when trying to remove a user from a workspace (group). I'm using the .Net SDK method DeleteUserAsAdminAsync. I tried the DeleteUserInGroupAsync method with the same result. I've had no trouble retrieving groups, reports, even adding users to groups using the same authentication code shown below. I'm quite stumped.
public async Task<ResultObject> RemoveUsersFromWorkspaces(List<PowerBIWorkspace> powerBIWorkspaces)
{
// Authenticate using created credentials
AuthenticationResult authenticationResult = null;
authenticationResult = await DoAuthentication("CustomerAppRegistration");
var tokenCredentials =
new TokenCredentials(authenticationResult.AccessToken, "Bearer");
using (var client = new PowerBIClient(
new Uri("https://api.powerbi.com/"), tokenCredentials))
{
try
{
foreach (var wksp in powerBIWorkspaces)
{
// Remove the user to the workspace.
await client.Groups.DeleteUserAsAdminAsync(new Guid(wksp.WorkspaceId), wksp.UserEmail);
}
}
catch (Exception ex)
{
var errorObject = new ResultObject();
errorObject.Success = false;
errorObject.ErrorMessage = ex.Message;
return errorObject;
}
}
var resultObject = new ResultObject();
resultObject.Success = true;
resultObject.ErrorMessage = "";
return resultObject;
}
private const string AuthorityFormat = "https://login.microsoftonline.com/{0}/v2.0";
private const string MSGraphScope = "https://analysis.windows.net/powerbi/api/.default";
private async Task<AuthenticationResult> DoAuthentication(string appRegistrationSection)
{
var config = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json").Build();
//var section = config.GetSection(nameof(AppRegistration));
var section = config.GetSection(appRegistrationSection);
var appRegistration = section.Get<AppRegistration>();
TenantID = appRegistration.TenantId;
ClientID = appRegistration.ClientId;
ClientSecret = appRegistration.ClientSecret;
IConfidentialClientApplication daemonClient;
daemonClient = ConfidentialClientApplicationBuilder.Create(ClientID)
.WithAuthority(string.Format(AuthorityFormat, TenantID))
.WithClientSecret(ClientSecret)
.Build();
AuthenticationResult authResult =
await daemonClient.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();
return authResult;
}
I'm programatically creating a workspace and importing a Direct-Query report into PowerBI Embedded V2. Everything works fine except the report data source credentials which are not updated.
This is the code for the import flow:
await _powerBIService.ImportPbixAsync(newWorkspaceId, reportNameWithoutExtension, fileStream);
Console.WriteLine("Imported report {0} for {1} ({2})", reportNameWithoutExtension, persona.Name, persona.Id);
string datasetId = null;
while (datasetId == null)
{
//get DataSource Id
Thread.Sleep(5000);
datasetId = await _powerBIService.GetDatasetIdFromWorkspace(newWorkspaceId, reportNameWithoutExtension);
}
//update the connection details
await _powerBIService.UpdateConnectionAsync(newWorkspaceId, datasetId, persona.SQLUser, persona.SQLPassword);
Console.WriteLine("Updated connection details for dataset {0}", reportNameWithoutExtension);
// get gateway ID
var gatewayId = await _powerBIService.GetGatewayIdFromWorkspaceAndDataset(newWorkspaceId, datasetId);
if (gatewayId != null)
{
//update credentials
await _powerBIService.UpdateGatewayDatasourcesCredentials(gatewayId, persona.SQLUser, persona.SQLPassword);
Console.WriteLine("Updated connection details for gateway {0}", reportNameWithoutExtension);
}
These are the individual methods:
public async Task UpdateConnectionAsync(string workspaceId, string datasetId, string sqlUser, string sqlPwd)
{
var bearerToken = await GetBearerTokenAsync();
var tokenCredentials = new TokenCredentials(bearerToken, "Bearer");
using (var client = new PowerBIClient(new Uri(_pbiApiUrl), tokenCredentials))
{
var dataSourcesResponse = await client.Datasets.GetDatasourcesInGroupAsync(workspaceId, datasetId);
var sqlDataSources = dataSourcesResponse.Value?.Where(s => s.DatasourceType == "Sql")
.Where(s => !s.ConnectionString.Contains(_reportsSQLServer) || !s.ConnectionString.Contains(_reportsSQLServer));
foreach (var sqlDataSource in sqlDataSources)
{
var updateDataSourceConnectionRequest = new UpdateDatasourceConnectionRequest();
updateDataSourceConnectionRequest.ConnectionDetails = new DatasourceConnectionDetails(_reportsSQLServer, _reportsSQLDatabase);
//updateDataSourceConnectionRequest.DatasourceSelector = new Datasource(datasourceId: sqlDataSource.DatasourceId);
var datasourcesRequest = new UpdateDatasourcesRequest();
datasourcesRequest.UpdateDetails = new List<UpdateDatasourceConnectionRequest>() { updateDataSourceConnectionRequest };
var result = await client.Datasets.UpdateDatasourcesInGroupAsync(workspaceId, datasetId, datasourcesRequest);
//await client.Gateways.UpdateDatasourceAsync()
}
}
}
public async Task UpdateGatewayDatasourcesCredentials(string gatewayId, string sqlUser, string sqlPassword)
{
var bearerToken = await GetBearerTokenAsync();
var tokenCredentials = new TokenCredentials(bearerToken, "Bearer");
using (var client = new PowerBIClient(new Uri(_pbiApiUrl), tokenCredentials))
{
var datasourcesResult = await client.Gateways.GetDatasourcesAsync(gatewayId);
var sqlGatewayDatasources = datasourcesResult.Value?
.Where(s => s.DatasourceType == "Sql")
.Where(s => s.ConnectionDetails.Contains(_reportsSQLServer) && s.ConnectionDetails.Contains(_reportsSQLDatabase));
foreach (var gatewayDatasource in sqlGatewayDatasources)
{
var updateDataSourceRequest = new UpdateDatasourceRequest();
updateDataSourceRequest.CredentialDetails = new CredentialDetails();
updateDataSourceRequest.CredentialDetails.CredentialType = "Basic";
updateDataSourceRequest.CredentialDetails.Credentials = "{\"credentialData\":[{\"name\":\"username\", \"value\":\"" + sqlUser + "\"},{\"name\":\"password\", \"value\":\"" + sqlPassword + "\"}]}";
updateDataSourceRequest.CredentialDetails.EncryptedConnection = "Encrypted";
updateDataSourceRequest.CredentialDetails.EncryptionAlgorithm = "None";
updateDataSourceRequest.CredentialDetails.PrivacyLevel = "None";
var result = await client.Gateways.UpdateDatasourceAsync(gatewayId, gatewayDatasource.Id, updateDataSourceRequest);
}
}
}
When I attempt to view the report there is no data being fetched from the database.
Interestingly I can login manually using the PowerBI portal using the same credentials. After the manual login the report is fetching data.
If after the manual login I delete the workspace and then recreate and re-import the report programatically then the report will show data. This makes me think there is some caching involved after the manual logic which is persisted outside of the workspace.
Any suggestions on what is missing here?
My OWIN Web.API 2 Hosted on EC2 will not authorize a JWT token. I have tested the functionality locally with out a problem but once I publish it out to my docker container hosted on EC2 it responds with a 401. I am using the default RS256 Algorithm and these settings:
var domain = Environment.GetEnvironmentVariable("AUTH0_DOMAIN");
var audience = Environment.GetEnvironmentVariable("AUTH0_CLIENT_IDS");
var keyResolver = new OpenIdConnectSigningKeyResolver(domain);
appBuilder.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
TokenValidationParameters = new TokenValidationParameters()
{
ValidAudience = audience,
ValidIssuer = domain,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) => keyResolver.GetSigningKey(identifier)
}
});
My Endpoint simply states whether your are authenticated or not.
[Authorize]
[Route("secure")]
public HttpResponseMessage GetSecured()
{
var userId = ClaimsPrincipal.Current.Identity.GetUserId();
return Request.CreateResponse($"Hello, {userId}! You are currently authenticated.");
}
Here is my Startup config:
public void Configuration(IAppBuilder appBuilder)
{
appBuilder.UseCors(CorsOptions.AllowAll); //must be first
Auth0Config.Register(appBuilder);
var httpConfiguration = new HttpConfiguration();
httpConfiguration.MapHttpAttributeRoutes();
UnityConfig.Register(httpConfiguration);
appBuilder.UseWebApi(httpConfiguration);
}
I no longer use the OWIN pipeline but from a previous project here is how I had it configured. It looks like you are using OpenID, I did not. Not sure if this helps.
var issuer = AppSettings.Auth0Domain;
var audience = AppSettings.Auth0ClientID;
var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["Auth0ClientSecret"]);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] {audience},
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
},
Provider = new Auth0AuthenticationProvider()
});
EDIT Added Auth0AuthenticationProvider
public class Auth0AuthenticationProvider : IOAuthBearerAuthenticationProvider
{
private string token;
public Task ApplyChallenge(OAuthChallengeContext context)
{
return Task.FromResult<object>(null);
}
public Task RequestToken(OAuthRequestTokenContext context)
{
token = context.Token;
return Task.FromResult<object>(null);
}
public Task ValidateIdentity(OAuthValidateIdentityContext context)
{
if (string.IsNullOrEmpty(token))
return Task.FromResult<object>(null);
var notPadded = token.Split('.')[1];
var padded = notPadded.PadRight(notPadded.Length + (4 - notPadded.Length % 4) % 4, '=');
var urlUnescaped = padded.Replace('-', '+').Replace('_', '/');
var claimsPart = Convert.FromBase64String(urlUnescaped);
var obj = JObject.Parse(Encoding.UTF8.GetString(claimsPart, 0, claimsPart.Length));
// simple, not handling specific types, arrays, etc.
foreach (var prop in obj.Properties().AsJEnumerable())
{
switch (prop.Name)
{
case "app_metadata":
SetAppMetadataClaims(context, prop.Value.ToString());
break;
}
}
return Task.FromResult<object>(null);
}
private static void SetAppMetadataClaims(OAuthValidateIdentityContext context, string jsonString)
{
var appMetadata = JsonConvert.DeserializeObject<Auth0AppMetaDataModel>(jsonString);
if(!context.Ticket.Identity.HasClaim("AccountId", appMetadata.accountId.ToString()))
context.Ticket.Identity.AddClaim(new Claim("AccountId", appMetadata.accountId.ToString()));
if (!context.Ticket.Identity.HasClaim("ClientId", appMetadata.clientId.ToString()))
context.Ticket.Identity.AddClaim(new Claim("ClientId", appMetadata.clientId.ToString()));
if (!context.Ticket.Identity.HasClaim("IsActive", appMetadata.isActive.ToString()))
context.Ticket.Identity.AddClaim(new Claim("IsActive", appMetadata.isActive.ToString()));
if (appMetadata.roles == null)
return;
foreach (var role in appMetadata.roles)
{
if (context.Ticket.Identity.HasClaim(ClaimTypes.Role, role))
continue;
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
}
The problem was not with Auth0 using HS256 or RS256 but instead with the base image I was using. Mono was failing silently most likely blocking the validation of my token. I have switched over to dotnet core in order to use the docker image: microsoft/dotnet:latest
My Startup file now appears like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors("CorsPolicy");
var options = new JwtBearerOptions
{
Audience = Configuration["Auth0:ApiIdentifier"],
Authority = $"https://{Configuration["Auth0:Domain"]}/"
};
app.UseJwtBearerAuthentication(options);
...
}
I'm trying to use AWS IAM to generate temporary tokens for a mobile app. I'm using the AWS C# SDK.
Here's my code...
The token generating service
public string GetIAMKey(string deviceId)
{
//fetch IAM key...
var credentials = new BasicAWSCredentials("MyKey", "MyAccessId");
var sts = new AmazonSecurityTokenServiceClient(credentials);
var tokenRequest = new GetFederationTokenRequest();
tokenRequest.Name = deviceId;
tokenRequest.Policy = File.ReadAllText(HostingEnvironment.MapPath("~/policy.txt"));
tokenRequest.DurationSeconds = 129600;
var tokenResult = sts.GetFederationToken(tokenRequest);
var details = new IAMDetails { SessionToken = tokenResult.GetFederationTokenResult.Credentials.SessionToken, AccessKeyId = tokenResult.GetFederationTokenResult.Credentials.AccessKeyId, SecretAccessKey = tokenResult.GetFederationTokenResult.Credentials.SecretAccessKey, };
return JsonConvert.SerializeObject(details);
}
The client
var iamkey = Storage.LoadPersistent<IAMDetails>("iamkey");
var simpleDBClient = new AmazonSimpleDBClient(iamkey.AccessKeyId, iamkey.SecretAccessKey, iamkey.SessionToken);
try
{
var details = await simpleDBClient.SelectAsync(new SelectRequest { SelectExpression = "select * from mydomain" });
return null;
}
catch (Exception ex)
{
Storage.ClearPersistent("iamkey");
}
The policy file contents
{ "Statement":[{ "Effect":"Allow", "Action":"sdb:* ", "Resource":"arn:aws:sdb:eu-west-1:* :domain/mydomain*" } ]}
I keep getting the following error...
User (arn:aws:sts::myaccountid:federated-user/654321) does not have permission to perform (sdb:Select) on resource (arn:aws:sdb:us-east-1:myaccountid:domain/mydomain)
Notice that my policy file clearly specifies two things
region should be eu-west-1
allowed action is a wild-card, ie, allow everything
But the exception thrown claims that my user doesn't have permission to us-east-1
Any ideas as to why I'm getting this error?
Ok figured it out.
You have to set the region endpoint on your call to the service from the client.
So
var simpleDBClient = new AmazonSimpleDBClient(iamkey.AccessKeyId, iamkey.SecretAccessKey, iamkey.SessionToken, Amazon.RegionEndpoint.EUWest1);