We're developing a multi-tenant SSO integration with Office 365 using the new OpenID implementation.
Once the access token has been retrieved, we call https://graph.windows.net/me?api-version=1.21-preview to get the user's profile information which gives something like the below - great.
{
"odata.metadata": "https:\/\/graph.windows.net\/myorganization\/$metadata#directoryObjects\/Microsoft.WindowsAzure.ActiveDirectory.User\/#Element",
"odata.type": "Microsoft.WindowsAzure.ActiveDirectory.User",
"objectType": "User",
"objectId": "GUID",
"accountEnabled": true,
"assignedLicenses": [
],
"assignedPlans": [
],
"city": null,
"country": null,
"department": null,
"dirSyncEnabled": null,
"displayName": "Tester A",
"facsimileTelephoneNumber": null,
"givenName": "Test",
"immutableId": null,
"jobTitle": null,
"lastDirSyncTime": null,
"mail": null,
"mailNickname": "tester-a",
"mobile": null,
"otherMails": [
],
"passwordPolicies": "None",
"passwordProfile": null,
"physicalDeliveryOfficeName": null,
"postalCode": null,
"preferredLanguage": null,
"provisionedPlans": [
],
"provisioningErrors": [
],
"proxyAddresses": [
],
"state": null,
"streetAddress": null,
"surname": "A",
"telephoneNumber": null,
"usageLocation": null,
"userPrincipalName": "tester-a#test.onmicrosoft.com",
"userType": "Member"
}
However, it doesn't seem to return any identifier or specific domain for the organization - other than the domain contained in the userPrincipalName field. Is there a better way to identify the organization (considering the organization may update test.onmicrosoft.com to a custom domain of test.microsoft.com)?
Indeed. Use the tenantDetails API (https://graph.windows.net/{tenantDomain}/tenantDetails?api-version={version}), to get the display name of the directory and all verified domains associated with it. Documented here: http://msdn.microsoft.com/en-us/library/azure/hh974467.aspx.
Below is the trimmed output for my tenant.
Hope this helps.
{
"odata.metadata": "https://graph.windows.net/dushyantgill.com/$metadata#directoryObjects/Microsoft.WindowsAzure.ActiveDirectory.TenantDetail",
"value": [
{
"odata.type": "Microsoft.WindowsAzure.ActiveDirectory.TenantDetail",
"objectType": "Company",
"objectId": "62e173e9-301e-423e-bcd4-29121ec1aa24",
"assignedPlans": [
{
"assignedTimestamp": "2013-09-17T01:01:58Z",
"capabilityStatus": "Enabled",
"service": "SharePoint",
"servicePlanId": "a1f3d0a8-84c0-4ae0-bae4-685917b8ab48"
} [SNIP]
],
"city": "Redmond",
"companyLastDirSyncTime": "2014-04-20T17:42:58Z",
"country": null,
"countryLetterCode": "US",
"dirSyncEnabled": true,
"displayName": "dushyantgill",
"marketingNotificationEmails": [],
"postalCode": "98052",
"preferredLanguage": "en",
"provisionedPlans": [
{
"capabilityStatus": "Enabled",
"provisioningStatus": "Success",
"service": "exchange"
}[SNIP]
],
"provisioningErrors": [],
"state": "WA",
"street": "[SNIP]",
"technicalNotificationMails": [
"[SNIP]"
],
"telephoneNumber": null,
"tenantType": null,
"verifiedDomains": [
{
"capabilities": "Email, OfficeCommunicationsOnline",
"default": false,
"id": "0005000080186A52",
"initial": false,
"name": "dushyantgill.mail.onmicrosoft.com",
"type": "Managed"
},
{
"capabilities": "Email, OfficeCommunicationsOnline",
"default": false,
"id": "00057FFE803C0EDA",
"initial": false,
"name": "dushyantgill.org",
"type": "Federated"
},
{
"capabilities": "Email, OfficeCommunicationsOnline",
"default": true,
"id": "00053FFF80232F54",
"initial": false,
"name": "dushyantgill.com",
"type": "Managed"
}[SNIP]
]
}
]
}
Related
I am using STRAPI with custom rich text editor. I setup from this docs
Now If I write more than 234 word I gives ValidationError in console.
I am trying to increase max text limit but I am not sure it is possible or not and from where we can increase word limit.
Here is I am using STRAPI version v3.6.2
{
"name": "ValidationError",
"value": {
"id": 1,
"title": "news1 edited",
"description": "<p>asssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddDDSDSFd</p>",
"published_at": "2021-11-17T07:19:43.837Z",
"created_by": {
"registrationToken": null,
"lastname": "user",
"preferedLanguage": null,
"blocked": null,
"resetPasswordToken": null,
"isActive": true,
"username": "username",
"id": 1,
"firstname": "user",
"email": "user#email.com"
},
"updated_by": {
"registrationToken": null,
"lastname": "user",
"preferedLanguage": null,
"blocked": null,
"resetPasswordToken": null,
"isActive": true,
"username": "username",
"id": 1,
"firstname": "user",
"email": "user#email.com"
},
"created_at": "2021-11-17T07:09:34.551Z",
"updated_at": "2021-12-06T03:22:38.714Z"
},
"errors": [
"components.Input.error.validation.maxLength"
],
"inner": [
{
"name": "ValidationError",
"value": "<p>asssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddDDSDSFd</p>",
"path": "description",
"type": "max",
"errors": [
"components.Input.error.validation.maxLength"
],
"inner": [],
"message": "components.Input.error.validation.maxLength",
"params": {
"path": "description",
"value": "<p>asssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddDDSDSFd</p>",
"originalValue": "<p>asssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddsdsdsdsdsdsdsdsdsdasssadsdsdsdsdsddDDSDSFd</p>",
"max": 240
}
}
],
"message": "components.Input.error.validation.maxLength"
}
We registered an app in Azure with our corporate account.
We've set it as a web-app type, and set the signInAudience to AzureADandPersonalMicrosoftAccount
It works when we sign into our web-app with a corporate account, but when we use a personal microsoft account with hotmail address, we get the following message:
AADSTS50020: User account '*********#hotmail.com' from identity provider 'live.com' does not exist in tenant '*********' and cannot access the application '******'(***-api) in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.
We double checked all settings and they look valid. We've ran out of ideas
Here's a version of our manifest:
{
"id": "**********",
"acceptMappedClaims": null,
"accessTokenAcceptedVersion": 2,
"addIns": [],
"allowPublicClient": null,
"appId": "*********",
"appRoles": [],
"oauth2AllowUrlPathMatching": false,
"createdDateTime": "2020-08-04T14:32:25Z",
"groupMembershipClaims": "None",
"identifierUris": [
"https://***.company.be"
],
"informationalUrls": {
"termsOfService": "https://www.company.be/legals/gebruiksvoorwaarden",
"support": null,
"privacy": "https://www.company.be/legals/privacy",
"marketing": null
},
"keyCredentials": [],
"knownClientApplications": [],
"logoUrl": "https://secure.aadcdn.microsoftonline-p.com/...",
"logoutUrl": null,
"name": "***-api",
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": false,
"oauth2Permissions": [],
"oauth2RequirePostResponse": false,
"optionalClaims": {
"idToken": [
{
"name": "email",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "family_name",
"source": null,
"essential": false,
"additionalProperties": []
},
{
"name": "given_name",
"source": null,
"essential": false,
"additionalProperties": []
}
],
"accessToken": [],
"saml2Token": []
},
"orgRestrictions": [],
"parentalControlSettings": {
"countriesBlockedForMinors": [],
"legalAgeGroupRule": "Allow"
},
"passwordCredentials": [
{
"customKeyIdentifier": null,
"endDate": "2299-12-30T23:00:00Z",
"keyId": "**********",
"startDate": "2020-08-04T14:47:56.759Z",
"value": null,
"createdOn": "2020-08-04T14:47:58.647043Z",
"hint": "gIh",
"displayName": "*****-api clientSecret"
}
],
"preAuthorizedApplications": [],
"publisherDomain": "company.be",
"replyUrlsWithType": [
{
"url": "http://localhost:3000/link/microsoft/callback",
"type": "Web"
},
{
"url": "https://api.copmpany.be/link/microsoft/callback",
"type": "Web"
},
{
"url": "https://api.copmpany.be/auth/microsoft/callback",
"type": "Web"
},
{
"url": "https://api.staging.copmpany.be/link/microsoft/callback",
"type": "Web"
},
{
"url": "https://api.staging.copmpany.be/auth/microsoft/callback",
"type": "Web"
},
{
"url": "http://localhost:3000/auth/microsoft/callback",
"type": "Web"
}
],
"requiredResourceAccess": [
{
"resourceAppId": "***",
"resourceAccess": [
{
"id": "****",
"type": "Scope"
},
{
"id": "***",
"type": "Scope"
},
{
"id": "***",
"type": "Scope"
},
{
"id": "***",
"type": "Scope"
},
{
"id": "***",
"type": "Scope"
}
]
}
],
"samlMetadataUrl": null,
"signInUrl": "https://www.company.be",
"signInAudience": "AzureADandPersonalMicrosoftAccount",
"tags": [
"webApp",
"notApiConsumer"
],
"tokenEncryptionKeyId": null,
"verifiedPublisher": {
"displayName": null,
"verifiedPublisherId": null,
"addedDateTime": null
}
}
In addition to Sarthak's answer if your business scenario doesn't need to access your tenant's resource like call MS Graph API for your tenant, You could change your authority Uri to common endpoint which supports MSA without inviting into tenant. Please refer the documentation on for the authority endpoints. Depending upon your business scenario, choose your solution.
From your manifest file I can see that your app is already registered as multi-tenant, so no need to change any settings there.
The user should be invited before we sign or connect with our azure active directory as an external user, follow the below steps:
Visit here https://portal.azure.com/#blade/Microsoft_AAD_IAM/UsersManagementMenuBlade/MsGraphUsers
Invite as a guest user
Then the invited user will get an invitation, accept that first.
Retry to access the web app.
Definitely it would work.
Sign in option AzureADandPersonalMicrosoftAccount only works with v2.0. Hence, the authority URL should be this:
https://login.microsoftonline.com/common/v2.0
In addition to that, for multitenant applications, ValidateIssuer option should be disabled. You can do that in this way:
auth.AddOpenIdConnect(authScheme, displayName, options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
)};
The app should be registered as multi-tenant for this to work.
I'm building a web app which sells several products, each product corresponds to a pricing plan. All the payment system is managed by Stripe.
It happens very often that a user has a subscription that contains Product A (i.e., one pricing plan), and then he wants to add Product B (i.e., another pricing plan) to the same subscription. I want to know how to achieve this by APIs of Stripe.
There is a webpage of Stripe to update a subscription (e.g., https://dashboard.stripe.com/test/subscriptions/sub_H4pQGW8nnc80vF/edit where sub_H4pQGW8nnc80vF is the subscription ID). Let's assume this customer already has 1 Verificator in the subscription, and then he wants to add 1 Pretty Formula to the same subscription. I do this update via the website. Here is the log:
Here is the full Request POST body:
{
"items": {
"0": {
"billing_thresholds": "",
"deleted": "false",
"id": "si_H4pQal4ZxGzLbW",
"quantity": "1",
"tax_rates": ""
},
"1": {
"billing_thresholds": "",
"plan": "plan_Gz6i9yPVIjrDPX",
"deleted": "false",
"quantity": "1"
}
},
"off_session": "true",
"prorate": "true",
"cancel_at": "",
"days_until_due": "30",
"default_tax_rates": "",
"collection_method": "send_invoice",
"billing_thresholds": "",
"enable_incomplete_payments": "false",
"invoice_settings": {
"description": "",
"send_hosted_payment_email": "true",
"supported_payment_methods": {
"ach_credit_transfer": "false",
"au_becs_debit": "false",
"bancontact": "false",
"card": "true",
"fpx": "false",
"giropay": "false",
"ideal": "false",
"jp_credit_transfer": "false",
"paper_check": "false",
"sepa_credit_transfer": "false",
"sofort": "false"
},
"custom_fields": "",
"footer": ""
},
"default_payment_method": "",
"default_source": ""
}
Here is the full Response body:
{
"id": "sub_H4pQGW8nnc80vF",
"object": "subscription",
"application_fee_percent": null,
"billing_cycle_anchor": 1586597833,
"billing_thresholds": null,
"cancel_at": null,
"cancel_at_period_end": false,
"canceled_at": null,
"collection_method": "send_invoice",
"created": 1586597833,
"current_period_end": 1589189833,
"current_period_start": 1586597833,
"customer": "5e575130651c5721d808d25b",
"customer_email": "sdtikply#gmail.com",
"customer_name": "Thomas Joseph",
"days_until_due": 30,
"default_payment_method": null,
"default_source": null,
"default_tax_rates": [
],
"discount": null,
"ended_at": null,
"invoice_settings": {
"send_hosted_payment_email": true,
"supported_payment_methods": {
"ach_credit_transfer": false,
"au_becs_debit": false,
"bancontact": false,
"card": true,
"fpx": false,
"giropay": false,
"ideal": false,
"jp_credit_transfer": false,
"paper_check": false,
"sepa_credit_transfer": false,
"sofort": false
}
},
"items": {
"object": "list",
"data": [
{
"id": "si_H4pQal4ZxGzLbW",
"object": "subscription_item",
"billing_thresholds": null,
"created": 1586597833,
"metadata": {
},
"plan": {
"id": "plan_Ga6n9yMYCDnHCu",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 500,
"amount_decimal": "500",
"billing_scheme": "per_unit",
"created": 1579512574,
"currency": "usd",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {
},
"name": "Verificator",
"nickname": "Verificator",
"owning_merchant": "acct_1CiOQBEV4K2GahYL",
"owning_merchant_info": "acct_1CiOQBEV4K2GahYL",
"product": "prod_Ga6mVdA8KXyZ8I",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 1,
"subscription": "sub_H4pQGW8nnc80vF",
"tax_rates": [
]
},
{
"id": "si_H82ES9BdIKZCNG",
"object": "subscription_item",
"billing_thresholds": null,
"created": 1587337381,
"metadata": {
},
"plan": {
"id": "plan_Gz6i9yPVIjrDPX",
"object": "plan",
"active": true,
"aggregate_usage": null,
"amount": 500,
"amount_decimal": "500",
"billing_scheme": "per_unit",
"created": 1585278262,
"currency": "usd",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {
},
"name": "Pretty Formula",
"nickname": "Pretty Formula",
"owning_merchant": "acct_1CiOQBEV4K2GahYL",
"owning_merchant_info": "acct_1CiOQBEV4K2GahYL",
"product": "prod_GxqkRFdI08DvyR",
"tiers": null,
"tiers_mode": null,
"transform_usage": null,
"trial_period_days": null,
"usage_type": "licensed"
},
"quantity": 1,
"subscription": "sub_H4pQGW8nnc80vF",
"tax_rates": [
]
}
],
"has_more": false,
"total_count": 2,
"url": "/v1/subscription_items?subscription=sub_H4pQGW8nnc80vF"
},
"latest_invoice": "in_1GWfm1EV4K2GahYLlmtUEISo",
"livemode": false,
"metadata": {
},
"next_pending_invoice_item_invoice": null,
"owning_merchant": "acct_1CiOQBEV4K2GahYL",
"owning_merchant_info": "acct_1CiOQBEV4K2GahYL",
"pause_collection": null,
"pending_invoice_item_interval": null,
"pending_setup_intent": null,
"pending_update": null,
"plan": null,
"quantity": null,
"schedule": null,
"start_date": 1586597833,
"status": "active",
"tax_percent": null,
"trial_end": null,
"trial_start": null
}
So my question is, how can I code in my backend (Node.js) to achieve exactly the same thing?
You just need to use the Update Subscription API and provide the items portion just as you're seeing above:
"items": {
"0": {
"billing_thresholds": "",
"deleted": "false",
"id": "si_H4pQal4ZxGzLbW",
"quantity": "1",
"tax_rates": ""
},
"1": {
"billing_thresholds": "",
"plan": "plan_Gz6i9yPVIjrDPX",
"deleted": "false",
"quantity": "1"
}
},
The first item is the existing Subscription Item (si_), and the second one is the new one you want to add.
I try to get the customer email after a payment on Checkout Stripe new interface. The JSON posted by stripe Webhook always send customer_email with null value.
The stripe Checkout page ask for customer email so I don't understand why Stripe send back this value to null.
Though, customer value is not null.
{
"id": "evt_1FItv8Kj5elW7ZcvEuY6",
"object": "event",
"api_version": "2019-03-14",
"created": 1568539286,
"data": {
"object": {
"id": "cs_test_123123123",
"object": "checkout.session",
"billing_address_collection": null,
"cancel_url": "https://www.example.fr/canceled",
"client_reference_id": null,
"customer": "cus_FoWzBx2yusHfs9",
"customer_email": null,
"display_items": [
{
"amount": 1000,
"currency": "eur",
"quantity": 1,
"sku": {
"id": "sku_1234567",
"object": "sku",
"active": true,
"attributes": {
"name": "Product test"
},
"created": 1568538814,
"currency": "eur",
"image": null,
"inventory": {
"quantity": null,
"type": "infinite",
"value": null
},
"livemode": false,
"metadata": {
},
"package_dimensions": null,
"price": 1000,
"product": "prod_FoWr00dX3",
"updated": 1568538814
},
"type": "sku"
}
],
"livemode": false,
"locale": null,
"mode": "payment",
"payment_intent": "pi_1FItj5elW70Z2",
"payment_method_types": [
"card"
],
"setup_intent": null,
"submit_type": null,
"subscription": null,
"success_url": "https://www.example.fr/success"
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
The email the customer entered is actually on the Customer object that the CheckoutSession links to. [0] The customer_email field is something else(it's the field that your code might have set to prefill an email into the Session).
So retrieve the Customer object from the API (cus_FoWzBx2yusHfs9) and check the email field there; or retrieve the Session object and expand the Customer field.
[0] - https://stripe.com/docs/api/customers/object#customer_object-email
Everything is working well when using the demo API. I followed the "go live" procedure, the API key is now 'live' and I updated all the settings for production.
When creating the envelope I receive this error message :
{"errorCode"=>"ACCOUNT_LACKS_PERMISSIONS", "message"=>"This Account lacks sufficient permissions."}
I tried to set embedded argument to false on the json but apparently it's not doing anything different.
{:embedded => false,:name => "XXX yyy",:email => "XXX#yyy.com",:role_name => "PropriƩtaire", :sign_here_tabs => ... }
Any idea on how to solve this ? Thanks !
EDIT : here is the JSON request :
{
"emailBlurb": "deijzed",
"emailSubject": "dzeidjezij",
"documents": [
{
"documentId": "1",
"name": "2017-11-09_21h_50m_52s.pdf"
}
],
"recipients": {
"signers": [
{
"accessCode": "",
"addAccessCodeToEmail": false,
"customFields": null,
"idCheckConfigurationName": null,
"idCheckInformationInput": null,
"inheritEmailNotificationConfiguration": false,
"note": null,
"phoneAuthentication": null,
"recipientAttachment": null,
"requireIdLookup": null,
"roleName": "Propri\u00c3\u00a9taire",
"routingOrder": 1,
"socialAuthentications": null,
"recipientId": 1,
"email": "xxx#company.com",
"name": "xxx yyy",
"autoNavigation": false,
"defaultRecipient": false,
"signatureInfo": null,
"tabs": {
"approveTabs": null,
"checkboxTabs": [
{
}
],
"companyTabs": null,
"dateSignedTabs": [
],
"dateTabs": null,
"declineTabs": null,
"emailTabs": [
],
"envelopeIdTabs": null,
"fullNameTabs": [
],
"listTabs": [
],
"noteTabs": null,
"numberTabs": [
],
"radioGroupTabs": [
{
}
],
"initialHereTabs": [
{
}
],
"signHereTabs": [
{
}
],
"signerAttachmentTabs": null,
"ssnTabs": null,
"textTabs": [
{
}],
"titleTabs": [
],
"zipTabs": null
}
},
{
"accessCode": "",
"addAccessCodeToEmail": false,
"customFields": null,
"idCheckConfigurationName": null,
"idCheckInformationInput": null,
"inheritEmailNotificationConfiguration": false,
"note": null,
"phoneAuthentication": null,
"recipientAttachment": null,
"requireIdLookup": null,
"roleName": "company",
"routingOrder": 2,
"socialAuthentications": null,
"recipientId": 2,
"email": "xxx#yyy.com",
"name": "xxx yyy",
"autoNavigation": false,
"defaultRecipient": false,
"signatureInfo": null,
"tabs": {
"approveTabs": null,
"checkboxTabs": [
],
"companyTabs": null,
"dateSignedTabs": [
{
}
],
"dateTabs": null,
"declineTabs": null,
"emailTabs": [
],
"envelopeIdTabs": null,
"fullNameTabs": [
],
"listTabs": [
],
"noteTabs": null,
"numberTabs": [
],
"radioGroupTabs": [
],
"initialHereTabs": [
{
}
],
"signHereTabs": [
{
}
],
"signerAttachmentTabs": null,
"ssnTabs": null,
"textTabs": [
],
"titleTabs": [
],
"zipTabs": null
}
}
],
"carbonCopies": [
]
},
"eventNotification": {
"useSoapInterface": false,
"includeCertificateWithSoap": false,
"url": "xxxxx",
"loggingEnabled": true,
"envelopeEvents": [
],
"recipientEvents": [
{
"includeDocuments": true,
"recipientEventStatusCode": "Completed"
}
]
},
"status": "sent",
"customFields": null
}
I can see you are using "eventNotification", so can you please check if DocuSign connect is supported in your subscription/plan which you bought with DocuSign. If its not available then you need to contact your DocuSign Account Manager for enabling this feature.
The issue was on my side : the API endpoint to be used for European accounts is not www.docusign but eu.docusign
Thanks again for your help