I am trying to post an adaptive card to Teams, I have provided App Id & password in appsettings.json.
I'm getting this exception:
clientId is null at Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential..ctor(String clientId, String clientSecret)
in D:\a\1\s\src\Microsoft.IdentityModel.Clients.ActiveDirectory\ClientCredential.cs:line 51
At this step:
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCards.AdaptiveCard.ContentType,
Content = card
};
replyToConversation.Attachments.Add(attachment);
var reply = await connector.Conversations.SendToConversationAsync(replyToConversation);
Related
Having read the documentation here I thought I should be able to add a user to active directory B2C and then be able to log in as that user. The error message is: "We can't seem to find your account"
[TestMethod]
public async Task CreateUserTest()
{
string mailNickname = Guid.NewGuid().ToString();
string upn = mailNickname + "#mydomain.onmicrosoft.com";
string email = "zzz#gmail.com";
User record = new User { Email = email, DisplayName = "Bob Smith", MailNickname = mailNickname, UserPrincipalName = upn };
record.Identities = new List<ObjectIdentity>();
record.PasswordProfile = new PasswordProfile();
record.Identities.Append(new ObjectIdentity { Issuer = "mydomain.onmicrosoft.com", IssuerAssignedId = email, ODataType = "microsoft.graph.objectidentity", SignInType = "emailAddress" });
record.Identities.Append(new ObjectIdentity { Issuer = "mydomain.onmicrosoft.com", IssuerAssignedId = upn, ODataType = "microsoft.graph.objectidentity", SignInType = "userPrincipalName" });
record.PasswordProfile.Password = "Abcdefgh123!!";
record.AccountEnabled = true;
record.PasswordProfile.ForceChangePasswordNextSignIn = false;
User user = await graphService.CreateUser(record);
Assert.IsNotNull(user);
}
public async Task<User> CreateUser(User user)
{
var result = await client.Users.Request().AddAsync(user);
return user;
}
This login code works if the user logs in using an existing account or creates a new one using the Sign up now link:
export const SignIn = async (appState: AppState): Promise<string> => {
var msg: string = '';
try {
const response = await MSAL.login('loginPopup');
Edit: Add screen cap showing user type and source:
I tried to create a consumer user with code like yours:
And tested with this account in user flow, it returned the token well:
Please check the accounts that you created in your code, the User type always need to be Member and have the Source Azure Active Directory.
I sent envelope at embed app without client_id. Then, I can check the envelope by email, and if I signed the envelope in the email, is there a way to get an event on the embed app?
public Map<String,Object> createRecipientView(Map<String,Object> recipientInfo) {
Map<String,Object> result = new HashMap<String,Object>();
// instantiate a new EnvelopesApi object
EnvelopesApi envelopesApi = new EnvelopesApi();
// set the url where you want the recipient to go once they are done signing
RecipientViewRequest view = new RecipientViewRequest();
//view.setReturnUrl("https://www.docusign.com");
view.setReturnUrl(serverUrl+"/"+recipientInfo.get("usr_cls")+"/docusign/returnRecipientView.do?ds_usr_id="+recipientInfo.get("ds_usr_id") + "&docusign_id=" + recipientInfo.get("docusign_id")+ "&usr_cls=" + recipientInfo.get("usr_cls") + "&uiId=" +recipientInfo.get("uiId"));
view.setAuthenticationMethod("email");
// recipient information must match embedded recipient info we provided in step #2
String email = (String)recipientInfo.get("email");
String userName = (String)recipientInfo.get("usr_nm");
String recipientId = (String)recipientInfo.get("recipient_id");
String clientId = (String)recipientInfo.get("client_id");
String envelopeId = (String)recipientInfo.get("envelope_id");
view.setEmail(email);
view.setUserName(userName);
view.setRecipientId(recipientId);
//view.seta
//view.setClientUserId(clientId);
// call the CreateRecipientView API
ViewUrl recipientView;
try {
recipientView = envelopesApi.createRecipientView(accountId, envelopeId, view);
log.info("Signing URL = " + recipientView.getUrl());
result.put("url", recipientView.getUrl());
result.put("result_status", "S");
} catch (ApiException e) {
log.error("error : " + e.toString());
result.put("result_status", "F");
result.put("result_message", e.toString());
}
return result;
}
Yes, you can use webhook to get notifications about the envelope to your app.
You can read this blog post about it.
You can also look at this code example.
Here is some C# code taken from here:
EnvelopeDefinition envelopeDefinition = new EnvelopeDefinition();
var eventNotification = new EventNotification();
\\ Set up the endpoint URL to call (it must be using HTTPS and at least TLS1.1 or higher)
eventNotification.Url = "https:\\myapp.somedomain.com";
\\ DocuSign will retry on failure if this is set
eventNotification.RequireAcknowledgment = "true";
\\ This would send the documents together with the event to the endpoint
eventNotification.IncludeDocuments = "true";
\\ Allows you to see this in the DocuSign Admin Connect logs section
eventNotification.LoggingEnabled = "true";
var envelopeEvents = new List<EnvelopeEvent>();
\\ In this case we only add a single envelope event, when the envelope is completed. You can also add events for recipients
envelopeEvents.Add(new EnvelopeEvent { EnvelopeEventStatusCode = "completed", IncludeDocuments = "true" });
eventNotification.EnvelopeEvents = envelopeEvents;
envelopeDefinition.EventNotification = eventNotification;
I have embedded docusign ui in my app.its working fine in lower environments.But user is being kick out from docusign ui within 4-5 seconds in docusign production.I know there is session timeout configuration ,it is configured 20 minutes.Anyone have faced this issue..any suggestion/help is appreciated
Retrieve docusign embedded url
public async Task<EmbeddedSigningUrls> BuildEmbeddedDocSigningUrlAsync(
string signerName, string signerEmail,
string redirectAfterSigningBaseUrl,
string envelopeId)
{
var viewOptions = new RecipientViewRequest()
{
ReturnUrl =
$"{redirectAfterSigningBaseUrl}?{RedirectUriBuilderParser.SigningOperationIdQueryParamName}={envelopeId}",
ClientUserId = signerEmail,
AuthenticationMethod = "password", // <<== not "email"
UserName = signerName,
Email = signerEmail
};
var envelopesApi = new EnvelopesApi();
var recipientView = await envelopesApi.CreateRecipientViewAsync(await AccountAsync(), envelopeId,
viewOptions);
return new EmbeddedSigningUrls
{
EmbeddedSigningUri = new Uri(recipientView.Url)
};
}
You need to update your code to this:
var viewOptions = new RecipientViewRequest()
{
ReturnUrl =
$"{redirectAfterSigningBaseUrl}?{RedirectUriBuilderParser.SigningOperationIdQueryParamName}={envelopeId}",
ClientUserId = signerEmail,
AuthenticationMethod = "none", // <<== not "email"
UserName = signerName,
Email = signerEmail
};
As your appliaction is just embedding a URL for the signing and is not adding additional authentication layer beyond the API calls that it is making.
What I am trying to do is to use the "Web API on-behalf-of flow" scenario Microsoft described in this article to create a web hook.
So I started with the Microsoft github example and made sure that I can successfully get the users profile via the Graph API.
Then I modified the code where it gets the users profile to create the web hook, so the code looks like this:
// Authentication and get the access token on behalf of a WPF desktop app.
// This part is unmodified from the sample project except for readability.
const string authority = "https://login.microsoftonline.com/mycompany.com";
const string resource = "https://graph.windows.net";
const string clientId = "my_client_id";
const string clientSecret = "my_client_secret";
const string assertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
var user = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var authenticationContext = new AuthenticationContext(authority,new DbTokenCache(user));
var assertion = ((BootstrapContext) ClaimsPrincipal.Current.Identities.First().BootstrapContext).Token;
var userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null
? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value
: ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
var result = await authenticationContext.AcquireTokenAsync(resource,new ClientCredential(clientId,clientSecret),new UserAssertion(assertion,assertionType,userName));
var accessToken = result.AccessToken;
// After getting the access token on behalf of the desktop WPF app,
// subscribes to get notifications when the user receives an email.
// This is the part that I put in.
var subscription = new Subscription
{
Resource = "me/mailFolders('Inbox')/messages",
ChangeType = "created",
NotificationUrl = "https://mycompany.com/subscription/listen",
ClientState = Guid.NewGuid().ToString(),
ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 4230, 0)
};
const string subscriptionsEndpoint = "https://graph.microsoft.com/v1.0/subscriptions/";
var request = new HttpRequestMessage(HttpMethod.Post, subscriptionsEndpoint);
var contentString = JsonConvert.SerializeObject(subscription, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
request.Content = new StringContent(contentString, System.Text.Encoding.UTF8, "application/json");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await new HttpClient().SendAsync(request);
if (response.IsSuccessStatusCode)
{
// Parse the JSON response.
var stringResult = await response.Content.ReadAsStringAsync();
subscription = JsonConvert.DeserializeObject<Subscription>(stringResult);
}
The error I get from the response is:
{
"error":
{
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.",
"innerError":
{
"request-id": "f64537e7-6663-49e1-8256-6e054b5a3fc2",
"date": "2017-03-27T02:36:04"
}
}
}
The webhook creation code was taken straight from the ASP.NET webhook github sample project, which, I have also made sure that I can run successfully.
The same access token code works with the original user profile reading code:
// Call the Graph API and retrieve the user's profile.
const string requestUrl = "https://graph.windows.net/mycompany.com/me?api-version=2013-11-08";
request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await new HttpClient().SendAsync(request);
So I want to find out:
Is creating a webhook via the graph API using the on-behalf-of flow even supported? Not sure if this SO question is what I'm looking for here.
If it is supported, what am I missing here?
If it is not supported, is there an alternative to achieve it? E.g. is there anything from the existing Office 365 API that I can use?
"message": "Access token validation failure.",
The error means you got incorrect access token for the resource . According to your code ,you get the access token for resource :https://graph.windows.net( Azure AD Graph API) , But then you used that access token to access Microsoft Graph API(https://graph.microsoft.com) ,so access token validation failed .
Apparently you can do this with the Facebook provider by adding scopes to the FacebookAuthenticationOptions object in Startup.Auth.cs:
http://blogs.msdn.com/b/webdev/archive/2013/10/16/get-more-information-from-social-providers-used-in-the-vs-2013-project-templates.aspx
List<string> scope = new List<string>() { "email" };
var x = new FacebookAuthenticationOptions();
x.Scope.Add("email");
...
app.UseFacebookAuthentication(x);
How to do the same with Google provider? There isn't a x.Scope property for the GoogleAuthenticationOptions class/object!
PLEASE SEE UPDATES AT THE BOTTOM OF THIS POST!
The following works for me for Facebook:
StartupAuth.cs:
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = "x",
AppSecret = "y"
};
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
ExternalLoginCallback method:
var externalIdentity = HttpContext.GetOwinContext().Authentication.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
And for Google:
StartupAuth.cs
app.UseGoogleAuthentication();
ExternalLoginCallback method (same as for facebook):
var externalIdentity = HttpContext.GetOwinContext().Authentication.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
var emailClaim = externalIdentity.Result.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email);
var email = emailClaim.Value;
If I set a breakpoint here:
var email = emailClaim.Value;
I see the email address for both Facebook and Google in the debugger.
Update 1: The old answer had me confused so I updated it with the code I have in my own project that I just debugged and I know works.
Update 2: With the new ASP.NET Identity 2.0 RTM version you no longer need any of the code in this post. The proper way to get the email is by simply doing the following:
Startup.Auth.cs
app.UseFacebookAuthentication(
appId: "x",
appSecret: "y");
app.UseGoogleAuthentication();
AccountController.cs
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInHelper.ExternalSignIn(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresTwoFactorAuthentication:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
You need to explicitly configure the FacebookAuthenticationOptions to get the email address from the authenticated user.
In your MVC5 project, add these lines in the Startup.Auth.cs
var options = new FacebookAuthenticationOptions() {
AppId = "xxxxxxxx",
AppSecret = "xxxxxxxxx"
};
options.Scope.Add("email");
app.UseFacebookAuthentication(options);
Update
Reduced my sample code to the absolute minimum. Your updated code works fine by the way, I have also tried it with both Facebook and Google.
In ASP.NET Core Facebook authentication the Facebook middleware seems to no longer pass in the email, even if you add it to the scope. You can work around it by using Facebook's Graph Api to request the email.
You can use any Facebook Graph Api client or roll your own, and use it to invoke the Graph api as follows:
app.UseFacebookAuthentication(options =>
{
options.AppId = Configuration["Authentication:Facebook:AppId"];
options.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
options.Scope.Add("public_profile");
options.Scope.Add("email");
options.Events = new OAuthEvents
{
OnCreatingTicket = context => {
// Use the Facebook Graph Api to get the user's email address
// and add it to the email claim
var client = new FacebookClient(context.AccessToken);
dynamic info = client.Get("me", new { fields = "name,id,email" });
context.Identity.AddClaim(new Claim(ClaimTypes.Email, info.email));
return Task.FromResult(0);
}
};
});
You can find a more detailed example about how to use it here: http://zainrizvi.io/2016/03/24/create-site-with-facebook-login-using-asp.net-core/#getting-the-email-address-from-facebook