Hi,
First I want to let you know that I'm beginner with Azure.
I have a problem to generate SAS-token (view-access token) for my blob storage container in my webapp server code. I'll get an 403 (unauthorized) error when trying to generate the token. I have a local server project, and nearly "cloned" project which is in my webapp server. The code works in my local server project, and generates SAS-token successfully, and I think the reason for that is I'm using my Azure - "owner" account, which have all rights to do what ever want to.
But when I try to generate SAS-token with my WebApp, it's resource role doesn't have rights to do that (regarding the 403 unauthorized error). I have assigned the "Storage Blob Data Contributor", "Storage Blob Data Owner", and "Storage Blob Delegator" roles in blob storage for my WebApp. I have also allowed my webapp's ip-addresses in the blob storage network-settings (images attached). I first used the blob storage access key in my code (with keyvault), and it worked on my local project. But I changed it later to use "DefaultAzureCredential()" in my code = to use recourse's own role (?).
When trying with "Enabled from selected virtual networks and IP addresses"-set up on my blob storage network settings, I'll get this error:
"Error uploading image: This request is not authorized to perform this operation.\nRequestId:b4047121-001e-0048-654e-79dc55000000\nTime:2024-03-18T16:11:01.2061918Z\r\nStatus: 403 (This request is not authorized to perform this operation.)\r\nErrorCode: AuthorizationFailure\r\n\r\nContent:\r\n<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>AuthorizationFailure</Code><Message>This request is not authorized to perform this operation.\nRequestId:b4047121-001e-0048-654e-79dc55000000\nTime:2024-03-18T16:11:01.2061918Z</Message></Error>\r\n\r\nHeaders:\r\nServer: Microsoft-HTTPAPI/2.0\r\nx-ms-request-id: b4047121-001e-0048-654e-79dc55000000\r\nx-ms-client-request-id: e00d19bd-a0f6-4ae7-8e1b-3b486154609b\r\nx-ms-error-code: AuthorizationFailure\r\nDate: Mon, 18 Mar 2024 16:11:00 GMT\r\nContent-Length: 246\r\nContent-Type: application/xml\r\n",
When I tried to allow all the traffic with "Enabled from all networks"-set up on my blob storage, I got this error:
"Error uploading image: Value cannot be null. (Parameter 'sharedKeyCredential')",
I have the same problem with Key Vault (works with local but not in webapp), but I would like to figure blob storage problem first. I have assigned these roles for webapp in my keyvault resource: "Key Vault Certificates Officer", "Key Vault Secrets User", and "Reader".
I'll give parts of my WebApp's Program.cs-class, and other classes where you can check if there is something wrong in my code.
It may be some stupid mistake, but I'm pretty new with these..
Thank you!
//PROGRAM.CS
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Perikato.Data;
using Perikato.Services;
using System.Net;
using System.Security.Claims;
using Microsoft.EntityFrameworkCore.Design;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.OpenApi.Models;
using MongoDB.Driver;
using System.Security.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Perikato.Services.MongoDbServices;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
using Azure.Storage.Blobs;
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
OIDC_Service OIDC_service = new OIDC_Service();
builder.Services.AddControllers();
builder.Services.AddLogging();
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Debug);
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
builder.Services.AddEndpointsApiExplorer();
ConfigurationManager configuration = builder.Configuration;
//Azure keys from KeyVault
var (identityTokenCertificate,
OIDC_client_id,
OIDC_client_secret,
PerikatoDBpass,
PerikatoMongoDBpass,
FirebaseServerKey,
AzureBlobStorageConnectionString) = OIDC_Service.ConnectToAzureKeyVault(builder.Configuration);
string firebaseServerKeyString = FirebaseServerKey.Value;
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromJson(firebaseServerKeyString),
});
string containerEndpoint = $"https://perikatoblobfiles.blob.core.windows.net/perikatopackageimages";
//Azure Blob Storage to DI-container
builder.Services.AddSingleton(x => new BlobServiceClient(new Uri(containerEndpoint), new DefaultAzureCredential()));
// MS-SQL
string connectionString = $"Server=tcp:perikatoserver.database.windows.net,1433;Initial Catalog=PerikatoDB;Persist Security Info=False;User ID=perikatodb;Password={PerikatoDBpass.Value};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
// MongoDB
string mongoDbConnectionString = $"mongodb://mongogeodb:{PerikatoMongoDBpass.Value}@mongogeodb.mongo.cosmos.azure.com:10255/?ssl=true&retrywrites=false&replicaSet=globaldb&maxIdleTimeMS=120000&appName=@mongogeodb@";
var mongoClientSettings = MongoClientSettings.FromConnectionString(mongoDbConnectionString);
mongoClientSettings.SslSettings = new SslSettings() { EnabledSslProtocols = SslProtocols.Tls12 };
// MongoDB
builder.Services.AddSingleton<IMongoClient>(serviceProvider => new MongoClient(mongoClientSettings));
string databaseName = configuration["MongoDbDatabaseName"];
builder.Services.AddSingleton(serviceProvider =>
new GoogleMapsLegsService(serviceProvider.GetRequiredService<IMongoClient>(), databaseName, "GoogleMapsLegs"));
builder.Services.AddSingleton(serviceProvider =>
new DealLocationsService(serviceProvider.GetRequiredService<IMongoClient>(), databaseName, "DealLocations"));
builder.Services.AddSingleton<MapMatchesService>();
builder.Services.AddScoped<BlobStorageService>();
builder.Services.AddScoped<FirebaseService>();
builder.Services.AddScoped<UserDataHandler>();
builder.Services.AddSingleton<IUserClaimsService, UserClaimsService>();
//CORS
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.WithOrigins("http://localhost:8080")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
builder.Services.AddHttpClient();
builder.Services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.CookieOptions);
});
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// Muut mahdolliset asetukset...
});
builder.Services.AddControllersWithViews();
var app = builder.Build();
app.UseExceptionHandler("/FTN/AuthenticationFailed");
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI();
app.UseCors();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.UseCookiePolicy();
app.UseForwardedHeaders();
app.Run();
// BLOBSTORAGESERVICE.CS
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Sas;
using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Threading.Tasks;
namespace Perikato.Services
{
public class BlobStorageService
{
private readonly BlobServiceClient _blobServiceClient;
public BlobStorageService(BlobServiceClient blobServiceClient)
{
_blobServiceClient = blobServiceClient;
}
public async Task<(string, string)> UploadImageAsync(IFormFile image, string containerName)
{
var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
var blobClient = containerClient.GetBlobClient(image.FileName);
var uploadResponse = string.Empty;
var sasUrl = string.Empty;
try
{
using (var stream = image.OpenReadStream())
{
await blobClient.UploadAsync(stream, new BlobHttpHeaders { ContentType = image.ContentType });
uploadResponse = blobClient.Uri.AbsoluteUri;
}
var sasBuilder = new BlobSasBuilder
{
BlobContainerName = containerName,
BlobName = image.FileName,
Resource = "b", // 'b' for blob
StartsOn = DateTimeOffset.UtcNow.AddMinutes(-5),
ExpiresOn = DateTimeOffset.UtcNow.AddDays(7),
};
sasBuilder.SetPermissions(BlobSasPermissions.Read);
sasUrl = blobClient.GenerateSasUri(sasBuilder).AbsoluteUri;
}
catch (Exception ex)
{
// Log error
uploadResponse = $"Error uploading image: {ex.Message}";
}
return (uploadResponse, sasUrl); //Error response will be saved to SQL
}
}
}
![User's image](https://learn-attachment.microsoft.com/api/attachments/230087fd-b6a0-4f9f-944b-2f98f3ede0d9?platform=QnA)
![User's image](https://learn-attachment.microsoft.com/api/attachments/0ea94676-c5e0-438c-896c-33a752c0fc83?platform=QnA)