Partner-Center-DotNet-Samples icon indicating copy to clipboard operation
Partner-Center-DotNet-Samples copied to clipboard

Non interactive user auth

Open Safirion opened this issue 4 years ago • 3 comments

Feature Request

Maybe I have missunderstand the SecureAppModel and what I looking for is already existing. The year before, when MFA was not mandatory, I could use User Auth by directly getting access token with user credential like this :

"Authentication": {
        "ApplicationId": "mypartnercenterappid",
        "ApplicationSecret": "mypartnercenterappsecret", 
        "ApplicationDomain": "xxxxxxxxx.onmicrosoft.com",
        "User": "[email protected]",
        "Password": "myuserpassword",
        "TenantId": "tenant of my organization",
        "Authority": "https://login.windows.net",
        "ResourceUrl": "https://graph.windows.net",
        "PartnerServiceApiRoot": "https://api.partnercenter.microsoft.com"
      }
        public async Task<IPartner> GetPartnerConnection()
        {
            if (_aggregatePartner != null && !_aggregatePartner.Credentials.IsExpired())
                return _aggregatePartner;
            PartnerService.Instance.ApiRootUrl = Configurations.Authentication.PartnerServiceApiRoot;

            IPartnerCredentials partnerCredentials = await PartnerCredentials.Instance.GenerateByUserCredentialsAsync(Configurations.Authentication.ApplicationId, await GetUserToken());
            _aggregatePartner = PartnerService.Instance.CreatePartnerOperations(partnerCredentials);
            
            return _aggregatePartner;
        }

        private async Task<AuthenticationToken> GetUserToken()
        {
            HttpResponseMessage response = await _client.PostAsync($"{Configurations.Authentication.Authority}/{Configurations.Authentication.TenantId}/oauth2/token", new FormUrlEncodedContent(new Dictionary<string, string>
            {
                { "scope", "openid" },
                { "grant_type", "password" },
                { "resource", Configurations.Authentication.PartnerServiceApiRoot },
                { "client_id", Configurations.Authentication.ApplicationId },
                { "client_secret", Configurations.Authentication.ApplicationSecret },
                { "username", Configurations.Authentication.User },
                { "password", Configurations.Authentication.Password }
            }));
            if (!response.IsSuccessStatusCode)
                throw new Exception($"Can't get partner center token for user \"{Configurations.Authentication.User}\"");
            JObject auth = JsonConvert.DeserializeObject<JObject>(await response.Content.ReadAsStringAsync());
            string accessToken = auth["access_token"].Value<string>();
            long expiresOn = auth["expires_on"].Value<long>();
            return new AuthenticationToken(accessToken, new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(expiresOn).ToLocalTime());
        }

It was so simple, now, since arround october I can't use my GetUserToken function because response.IsSuccessStatusCode is false.

My application is an API that allow our customer's users to do actions on partner centers like : listing there users account and add/remove licence on user account. For this, application auth is not sufficient, so I have to use User Auth. I want to use a service account that auto-login in background as before. Is there any solution ?

Safirion avatar Jan 27 '21 16:01 Safirion

I realize this is a year old and I hope you managed to solve your problem. We had the same issue and it caused quite a bit of problem when MFA and secure model got adopted as we needed each of our employees to authorize every request we were automating.

We solved this by having a user authorize once, storing their token in the Azure Key Vault then having the refresh token used to keep doing automated tasks to the partner center in the background.

https://docs.microsoft.com/en-us/partner-center/develop/partner-center-authentication#partner-consent

This describes the process we followed hope it helps for others!

jhughes17 avatar Mar 08 '22 23:03 jhughes17

Feature Request

Maybe I have missunderstand the SecureAppModel and what I looking for is already existing. The year before, when MFA was not mandatory, I could use User Auth by directly getting access token with user credential like this :

"Authentication": {
        "ApplicationId": "mypartnercenterappid",
        "ApplicationSecret": "mypartnercenterappsecret", 
        "ApplicationDomain": "xxxxxxxxx.onmicrosoft.com",
        "User": "[email protected]",
        "Password": "myuserpassword",
        "TenantId": "tenant of my organization",
        "Authority": "https://login.windows.net",
        "ResourceUrl": "https://graph.windows.net",
        "PartnerServiceApiRoot": "https://api.partnercenter.microsoft.com"
      }
        public async Task<IPartner> GetPartnerConnection()
        {
            if (_aggregatePartner != null && !_aggregatePartner.Credentials.IsExpired())
                return _aggregatePartner;
            PartnerService.Instance.ApiRootUrl = Configurations.Authentication.PartnerServiceApiRoot;

            IPartnerCredentials partnerCredentials = await PartnerCredentials.Instance.GenerateByUserCredentialsAsync(Configurations.Authentication.ApplicationId, await GetUserToken());
            _aggregatePartner = PartnerService.Instance.CreatePartnerOperations(partnerCredentials);
            
            return _aggregatePartner;
        }

        private async Task<AuthenticationToken> GetUserToken()
        {
            HttpResponseMessage response = await _client.PostAsync($"{Configurations.Authentication.Authority}/{Configurations.Authentication.TenantId}/oauth2/token", new FormUrlEncodedContent(new Dictionary<string, string>
            {
                { "scope", "openid" },
                { "grant_type", "password" },
                { "resource", Configurations.Authentication.PartnerServiceApiRoot },
                { "client_id", Configurations.Authentication.ApplicationId },
                { "client_secret", Configurations.Authentication.ApplicationSecret },
                { "username", Configurations.Authentication.User },
                { "password", Configurations.Authentication.Password }
            }));
            if (!response.IsSuccessStatusCode)
                throw new Exception($"Can't get partner center token for user \"{Configurations.Authentication.User}\"");
            JObject auth = JsonConvert.DeserializeObject<JObject>(await response.Content.ReadAsStringAsync());
            string accessToken = auth["access_token"].Value<string>();
            long expiresOn = auth["expires_on"].Value<long>();
            return new AuthenticationToken(accessToken, new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(expiresOn).ToLocalTime());
        }

It was so simple, now, since arround october I can't use my GetUserToken function because response.IsSuccessStatusCode is false.

My application is an API that allow our customer's users to do actions on partner centers like : listing there users account and add/remove licence on user account. For this, application auth is not sufficient, so I have to use User Auth. I want to use a service account that auto-login in background as before. Is there any solution ?

Have you figure out how to change the console app sdk to be non interactive? Could you share how you did it?

RommelWZ avatar Aug 16 '22 10:08 RommelWZ

I have give up and just generate a token/refresh token manually first time.

Then I have stock the access token/refresh token in a SQL table and my app update theses tokens automatically when they expire.

A proper solution may exists but I don't found

Safirion avatar Aug 16 '22 11:08 Safirion