├── .gitignore ├── README.md ├── classes ├── Instagram.cls ├── InstagramMockHttpResponseGenerator.cls ├── InstagramRegHandler.cls ├── InstagramRegHandlerTestClass.cls └── InstagramTestClass.cls ├── customMetadata └── Instagram_Identity.Instagram.md ├── layouts └── Instagram_Identity__mdt-Instagram Identity Layout.layout ├── objects └── Instagram_Identity__mdt.object └── package.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | classes/Instagram.cls-meta.xml 3 | 4 | classes/InstagramMockHttpResponseGenerator.cls-meta.xml 5 | 6 | classes/InstagramRegHandler.cls-meta.xml 7 | 8 | classes/InstagramTestClass.cls-meta.xml 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | InstagramAuthProvider 2 | ===================== 3 | 4 | A custom [Auth Provider](https://help.salesforce.com/articleView?id=sso_authentication_providers.htm&type=5) for Instagram. 5 | 6 | March 2023 Update 7 | ----------------- 8 | Because Instagram has retired its Identity Provider capability and folded it into Facebook's feature set, this custom auth. provider will no longer work. It is still relevant as an example on how to build custom auth. providers. For an up-to-date example and tutorial please check out [Create a Custom External Authentication Provider](https://help.salesforce.com/s/articleView?id=sf.sso_provider_plugin_custom.htm&type=5). 9 | 10 | Overview 11 | -------- 12 | This project provides an example custom Auth Provider for Instagram's now defunct IdP. 13 | 14 | Getting Started 15 | --------------- 16 | Use the [Salesforce Migration Tool](https://developer.salesforce.com/docs/atlas.en-us.daas.meta/daas/meta_development.htm) or Workbench to install this unpackaged Apex. It will create a Custom Metadata Type as well as an Apex Class for handling the flow with Instagram, and an example Registration Handler as well. 17 | 18 | You will need to create a new Instagram app in their [developer environment](https://www.instagram.com/developer/) in order to get a key and secret. 19 | 20 | Once you have a key and secret, you can create your Auth Provider instance. Recommend that you create the Auth Provider, then copy the Callback URL the system generates and paste it into the 'Redirect URL (Callback URL)' field. You need to do this because you must register your callback URLs with Instagram and, for now, custom Auth Providers do not have direct access to their associated Callback URL values. 21 | 22 | Some things to keep in mind 23 | -------------------------- 24 | Instagam's user model is simple; it doesn't have a lot of attributes. For example, Instagram *does not* have email addresses for its users. (This is true even if the person uses Facebook to log into Instagram.) Because of this, the example Registration Handler has to create an email-like attribute for both the User's email and username fields. 25 | 26 | Easier Install Process 27 | -------- 28 | Although the Apex classes are provided here, you can also use the [this unmanaged package](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t1J000000PzH1) to load this custom Auth Provider into your org. 29 | -------------------------------------------------------------------------------- /classes/Instagram.cls: -------------------------------------------------------------------------------- 1 | global class Instagram extends Auth.AuthProviderPluginClass { 2 | 3 | public String redirectUrl; // use this URL for the endpoint that the authentication provider calls back to for configuration 4 | private String key; 5 | private String secret; 6 | private String authUrl; // application redirection to the Instgram website for authentication and authorization 7 | private String accessTokenUrl; // uri to get the new access token from Instagram using the GET verb 8 | private String customMetadataTypeApiName; // api name for the custom metadata type created for this auth provider 9 | private String userAPIUrl; // api url to access the user in Instagram 10 | private String userAPIVersionUrl; // version of the user api url to access data from Instagram 11 | 12 | global String getCustomMetadataType() { 13 | return 'Instagram_Identity__mdt'; 14 | } 15 | 16 | global PageReference initiate(Map authProviderConfiguration, String stateToPropagate) { 17 | authUrl = authProviderConfiguration.get('Auth_URL__c'); 18 | key = authProviderConfiguration.get('clientID__c'); 19 | redirectUrl = authProviderConfiguration.get('redirectURL__c'); 20 | String url = authUrl + '?client_id='+ key +'&response_type=code&redirect_uri='+ redirectUrl + '&state=' + stateToPropagate; 21 | 22 | return new PageReference(url); 23 | } 24 | 25 | global Auth.AuthProviderTokenResponse handleCallback(Map authProviderConfiguration, Auth.AuthProviderCallbackState state ) { 26 | key = authProviderConfiguration.get('clientID__c'); 27 | secret = authProviderConfiguration.get('clientSecret__c'); 28 | accessTokenUrl = authProviderConfiguration.get('Access_Token_URL__c'); 29 | redirectURL = authProviderConfiguration.get('redirectURL__c'); 30 | 31 | Map queryParams = state.queryParameters; 32 | String code = queryParams.get('code'); 33 | String sfdcState = queryParams.get('state'); 34 | 35 | Http http = new Http(); 36 | HttpRequest req = new HttpRequest(); 37 | String url = accessTokenUrl; 38 | req.setEndpoint(url); 39 | req.setHeader('Content-Type','application/x-www-form-urlencoded'); 40 | req.setMethod('POST'); 41 | //Instragram expects these values post'ed as if in a form 42 | req.setBody('client_id=' + key + '&client_secret=' + secret + '&redirect_uri=' + redirectURL + '&grant_type=authorization_code' + '&code=' + code); 43 | 44 | HTTPResponse res = http.send(req); 45 | String responseBody = res.getBody(); 46 | Map responseMap = (Map)JSON.deserializeUntyped(responseBody); 47 | String token = (String)responseMap.get('access_token'); 48 | 49 | return new Auth.AuthProviderTokenResponse('Instagram', token, null, sfdcState); 50 | } 51 | 52 | 53 | global Auth.UserData getUserInfo(Map authProviderConfiguration, Auth.AuthProviderTokenResponse response) { 54 | //Here the developer is responsible for constructing an Auth.UserData object 55 | String token = response.oauthToken; 56 | HttpRequest req = new HttpRequest(); 57 | userAPIUrl = authProviderConfiguration.get('UserEndpoint__c'); 58 | 59 | // Instagram requires access token in the query string 60 | req.setEndpoint(userAPIUrl + '?access_token=' + token); 61 | req.setHeader('Content-Type','application/json'); 62 | req.setMethod('GET'); 63 | 64 | Http http = new Http(); 65 | HTTPResponse res = http.send(req); 66 | String responseBody = res.getBody(); 67 | 68 | Map responseMap = (Map)JSON.deserializeUntyped(responseBody); 69 | Map userMap = (Map)responseMap.get('data'); 70 | 71 | String id = (String)userMap.get('id'); 72 | String username = (String)userMap.get('username'); 73 | String profilePicture = (String)userMap.get('profile_picture'); 74 | String website = (String)userMap.get('website'); 75 | String bio = (String)userMap.get('bio'); 76 | String fullName = (String)userMap.get('full_name'); 77 | Map provMap = new Map(); 78 | provMap.put('profilePicture', profilePicture); 79 | provMap.put('bio', bio); 80 | 81 | return new Auth.UserData(id, null, null, fullName, null, website, username,null, 'Instagram', null, provMap); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /classes/InstagramMockHttpResponseGenerator.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | global class InstagramMockHttpResponseGenerator implements HttpCalloutMock { 3 | // Implement this interface method 4 | global HTTPResponse respond(HTTPRequest req) { 5 | String namespace = InstagramTestClass.userEndpoint; 6 | String prefix = 'mockPrefix'; 7 | 8 | JSONGenerator gen = JSON.createGenerator(true); 9 | gen.writeStartObject(); 10 | gen.writeFieldName('data'); 11 | gen.writeStartObject(); 12 | gen.writeobjectfield('id', InstagramTestClass.id); 13 | gen.writeobjectfield('username', InstagramTestClass.username); 14 | gen.writeobjectfield('full_name', InstagramTestClass.fullName); 15 | gen.writeobjectfield('bio', InstagramTestClass.bio); 16 | gen.writeobjectfield('website', InstagramTestClass.website); 17 | gen.writeobjectfield('profile_picture', InstagramTestClass.profilePicture); 18 | gen.writeobjectfield('locale', ''); 19 | gen.writeendobject(); 20 | gen.writeobjectfield('access_token', InstagramTestClass.oauthToken); 21 | gen.writeendobject(); 22 | 23 | 24 | // Create a fake response 25 | HttpResponse res = new HttpResponse(); 26 | res.setHeader('Content-Type', 'application/json'); 27 | res.setBody(gen.getAsString()); 28 | res.setStatusCode(200); 29 | return res; 30 | } 31 | } -------------------------------------------------------------------------------- /classes/InstagramRegHandler.cls: -------------------------------------------------------------------------------- 1 | /** 2 | Registration Handler for Instagram 3 | **/ 4 | global class InstagramRegHandler implements Auth.RegistrationHandler{ 5 | 6 | public static String ORG_SUFFIX = '@user.instagram.com'; 7 | private static final String DEFAULT_ACCOUNTNAME = 'Customers'; 8 | public static String EXTERNAL_USER_PROFILE = 'Customer Community User'; 9 | public static String TZSID = [SELECT timezonesidkey from User where profile.name = 'System Administrator' LIMIT 1].timezonesidkey; 10 | 11 | 12 | /** 13 | * Create the User - A required method to implement the Handler Interface 14 | * 15 | * @param portalId - Id of the Community 16 | * @param data - Auth Provider user data describing the User to create 17 | * 18 | * @return User that has been initialized 19 | **/ 20 | global User createUser(Id portalId, Auth.UserData data){ 21 | // optimized for Communities 22 | 23 | System.debug('Dumping Auth.UserData: ' + data); 24 | System.debug('Registering Community user: ' + data.username); 25 | 26 | // keeping it modular, the marshal the data from Instagram in another method 27 | Auth.UserData d = normalizeUserData(data); 28 | 29 | Id contactId; 30 | contactId = createContact(d); 31 | System.debug('Created contact: '+ contactId); 32 | 33 | // You'd likely use other logic to assign the Profile 34 | Profile p = [SELECT Id FROM profile WHERE name=:EXTERNAL_USER_PROFILE]; 35 | System.debug('Found profile: '+ p); 36 | 37 | // Keeping it modular, we initialize the user in another method 38 | User u = createUser(d,p); 39 | 40 | u.contactId = contactId; 41 | return u; 42 | } 43 | 44 | /** 45 | * Update the user 46 | * @param portalId - Id of the Community 47 | * @param data - Auth Provider user data describing the User to create 48 | **/ 49 | global void updateUser(Id userId, Id portalId, Auth.UserData data){ 50 | System.debug('Update User called for: ' + data.email); 51 | 52 | User u = new User(id=userId); 53 | u.email = data.email; 54 | u.lastName = data.lastName; 55 | u.firstName = data.firstName; 56 | update(u); 57 | } 58 | /** 59 | * Create a Contact 60 | * 61 | * @param data - Instagram provided context for the User 62 | **/ 63 | 64 | private Auth.UserData normalizeUserData(Auth.UserData data){ 65 | // Instagram doesn't have a lot of user profile data 66 | // it lacks email address for example 67 | // use this method to fill in the holes 68 | 69 | // handle name 70 | String fullname = data.fullName; 71 | List names = fullname.split(' ', 2); 72 | if (names.size() >= 2){ 73 | data.firstName = names[0]; 74 | data.lastName = names[1]; 75 | } else { 76 | data.LastName = fullname; 77 | data.FirstName = 'Unknown'; 78 | } 79 | 80 | // handle email 81 | data.email = data.username + ORG_SUFFIX; 82 | 83 | return data; 84 | } 85 | 86 | private Id createContact(Auth.UserData data){ 87 | Contact c = new Contact(); 88 | 89 | c.email = data.email; 90 | c.FirstName = data.FirstName; 91 | c.LastName = data.LastName; 92 | 93 | c.Description = data.attributeMap.get('bio'); 94 | 95 | // set Account Id 96 | List accounts = [select Id from Account where Name =:DEFAULT_ACCOUNTNAME]; 97 | 98 | System.debug('Found account: ' + accounts); 99 | 100 | c.accountId = accounts[0].Id; 101 | insert c; 102 | 103 | System.debug('Contact created for ' + data.email + ' id=' + c.id); 104 | 105 | return c.id; 106 | } 107 | 108 | 109 | /** 110 | * Create and initialize the User but don't save it yet 111 | * 112 | * @param data - the provided User context from Instagram 113 | * @param p - the Profile we are going to assign to this user 114 | * 115 | * @return User that has been initialized but not Saved 116 | **/ 117 | private User createUser(Auth.UserData data, Profile p) { 118 | User u = new User(); 119 | 120 | u.email = data.email; 121 | u.username = u.email; 122 | u.FirstName = data.FirstName; 123 | u.LastName = data.LastName; 124 | 125 | String alias = data.username; 126 | //Alias must be 8 characters or less 127 | if(alias.length() > 8) { 128 | alias = alias.substring(0, 8); 129 | } 130 | u.alias = alias; 131 | 132 | u.languagelocalekey = UserInfo.getLocale(); 133 | u.localesidkey = UserInfo.getLocale(); 134 | u.emailEncodingKey = 'UTF-8'; 135 | u.timeZoneSidKey = TZSID; 136 | u.profileId = p.Id; 137 | 138 | System.debug('User staged for ' + u.username); 139 | return u; 140 | } 141 | } -------------------------------------------------------------------------------- /classes/InstagramRegHandlerTestClass.cls: -------------------------------------------------------------------------------- 1 | @isTest 2 | public class InstagramRegHandlerTestClass { 3 | public static String bio = 'a heartwarming backstory'; 4 | public static String profilePicture = 'https://picture.test.website/123456788.png'; 5 | public static String id = 'testId'; 6 | public static String fullName = 'Thomas Jones-Drew'; 7 | public static String firstName = 'Thomas'; 8 | public static String lastName = 'Jones-Drew'; 9 | public static String website = 'https://iam.test.website'; 10 | public static String username = 'testusername'; 11 | public static String ORG_SUFFIX = '@user.instagram.com'; 12 | 13 | /** 14 | * Simple scenario to create a Community user 15 | **/ 16 | testmethod public static void testCreateUser() { 17 | InstagramRegHandler handler = new InstagramRegHandler(); 18 | 19 | Auth.UserData data = createUser(); 20 | Account acct = new Account(); 21 | User u; 22 | User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()]; 23 | 24 | // Insert account as current user 25 | 26 | System.runAs (thisUser) { 27 | acct.Name = 'Customers'; 28 | insert(acct); 29 | 30 | User theUser = handler.createUser(null, data); 31 | 32 | validate(theUser,data); 33 | 34 | // Additional validations for Community User 35 | System.Assert(theUser.ContactId!=null,'Contact must be set for user'); 36 | } 37 | 38 | 39 | } 40 | 41 | /** 42 | * Simple direct test of the UpdateUser method. Create the user first 43 | * and then attempt to update some properties. 44 | **/ 45 | testMethod public static void testUpdateUser() { 46 | InstagramRegHandler handler = new InstagramRegHandler(); 47 | Auth.UserData data = createUser(); 48 | Account acct = new Account(); 49 | User u; 50 | User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()]; 51 | System.runAs (thisUser) { 52 | acct.Name = 'Customers'; 53 | insert(acct); 54 | 55 | User theUser = handler.createUser(null, data); 56 | insert theUser; 57 | 58 | // validate(theUser,data); 59 | data.firstName='Tom'; 60 | handler.updateUser(theUser.id, null, data); 61 | 62 | User theUpdatedUser = [SELECT Id,firstName,Email,LastName 63 | from User 64 | Where Id = :theUser.id]; 65 | 66 | validate(theUpdatedUser,data); 67 | } 68 | } 69 | 70 | /** 71 | * Helper method to Validate the the User we've created 72 | * 73 | * @param theUser - the User that we created 74 | * @param data - the original AuthData supplied by Instagram 75 | **/ 76 | private static void validate(User theUser, Auth.UserData data) { 77 | System.Assert(theUser!=null,'User must not be null'); 78 | System.AssertEquals(theUser.email,data.email,'Email address must be the same'); 79 | System.AssertEquals(theUser.FirstName,data.FirstName,'First name must match'); 80 | System.AssertEquals(theUser.LastName,data.LastName,'Last name must match'); 81 | } 82 | 83 | 84 | /** 85 | * Helper method to instantiate the handler UserData 86 | **/ 87 | private static Auth.UserData createUser() { 88 | Map attributeMap = new Map(); 89 | attributeMap.put('bio', bio); 90 | attributeMap.put('profilePicture', profilePicture); 91 | String locale = 'en_US'; 92 | return new Auth.UserData( id, 93 | firstName, 94 | lastName, 95 | fullName, // fullname 96 | username + ORG_SUFFIX, //email 97 | website, // link 98 | username, // userName 99 | locale, 100 | 'Instagram', // provider 101 | '', // siteLoginUrl 102 | attributeMap); 103 | } 104 | } -------------------------------------------------------------------------------- /classes/InstagramTestClass.cls: -------------------------------------------------------------------------------- 1 | @IsTest 2 | public class InstagramTestClass { 3 | public static String redirectUrl = 'http://localhost/services/authcallback/orgId/Instagram'; 4 | public static String key = 'testKey'; 5 | public static String secret = 'testSecret'; 6 | public static String stateToPropagate = 'testState'; 7 | public static String accessTokenUrl = 'http://www.dummyhost.com/accessTokenUri'; 8 | public static String authUrl = 'http://www.dummy.com/authurl'; 9 | public static String oauthToken = 'testToken'; 10 | public static String state = 'mocktestState'; 11 | public static String id = 'testId'; 12 | public static String username = 'testUsername'; 13 | public static String fullName = 'Test FullName'; 14 | public static String bio = 'Test bio which is very interesting'; 15 | public static String website = 'https://iam.test.website'; 16 | public static String profilePicture = 'https://picture.test.website/123456788.png'; 17 | public static String provider = 'Instagram'; 18 | public static String userEndpoint = 'https://api.instagram.com/v1/users/self/'; 19 | public static String userAPIVersionUrl = ''; 20 | 21 | 22 | // in the real world scenario , the key and value would be read from the custom metadata type record 23 | private static Map setupAuthProviderConfig () { 24 | Map authProviderConfiguration = new Map(); 25 | authProviderConfiguration.put('clientID__c',key); 26 | authProviderConfiguration.put('Auth_URL__c',authUrl); 27 | authProviderConfiguration.put('clientSecret__c',secret); 28 | authProviderConfiguration.put('Access_Token_URL__c',accessTokenUrl); 29 | authProviderConfiguration.put('UserEndpoint__c',userEndpoint); 30 | authProviderConfiguration.put('redirectURL__c',redirectUrl); 31 | 32 | return authProviderConfiguration; 33 | } 34 | static testMethod void testInitiateMethod() { 35 | Instagram instagramCls = new Instagram(); 36 | Map authProviderConfiguration = setupAuthProviderConfig(); 37 | 38 | PageReference expectedUrl = new PageReference(authUrl + '?client_id='+ key +'&response_type=code&redirect_uri='+ redirectUrl + '&state=' + stateToPropagate); 39 | PageReference actualUrl = instagramCls.initiate(authProviderConfiguration,stateToPropagate); 40 | 41 | System.assertEquals(expectedUrl.getUrl(),actualUrl.getUrl()); 42 | } 43 | 44 | static testMethod void testHandleCallback() { 45 | Instagram instagramCls = new Instagram(); 46 | Map authProviderConfiguration = setupAuthProviderConfig(); 47 | Test.setMock(HttpCalloutMock.class, new InstagramMockHttpResponseGenerator()); 48 | 49 | Map queryParams = new Map(); 50 | queryParams.put('code','code'); 51 | queryParams.put('state',state); 52 | 53 | Auth.AuthProviderCallbackState cbState = new Auth.AuthProviderCallbackState(null,null,queryParams); 54 | Auth.AuthProviderTokenResponse actualAuthProvResponse = instagramCls.handleCallback(authProviderConfiguration, cbState); 55 | Auth.AuthProviderTokenResponse expectedAuthProvResponse = new Auth.AuthProviderTokenResponse('Instagram', oauthToken, null, state); 56 | 57 | System.assertEquals(expectedAuthProvResponse.provider, actualAuthProvResponse.provider); 58 | System.assertEquals(expectedAuthProvResponse.oauthToken, actualAuthProvResponse.oauthToken); 59 | System.assertEquals(expectedAuthProvResponse.oauthSecretOrRefreshToken, actualAuthProvResponse.oauthSecretOrRefreshToken); 60 | System.assertEquals(expectedAuthProvResponse.state,actualAuthProvResponse.state); 61 | } 62 | 63 | static testMethod void testGetUserInfo(){ 64 | Instagram instagramCls = new Instagram(); 65 | Map authProviderConfiguration = setupAuthProviderConfig(); 66 | Test.setMock(HttpCalloutMock.class, new InstagramMockHttpResponseGenerator()); 67 | Map queryParams = new Map(); 68 | queryParams.put('code','code'); 69 | queryParams.put('state',state); 70 | 71 | Auth.UserData actualUserData = instagramCls.getUserInfo(authProviderConfiguration, new Auth.AuthProviderTokenResponse('Instagram', oauthToken, null, state)); 72 | Map attributeMap = new Map(); 73 | attributeMap.put('bio', bio); 74 | attributeMap.put('profilePicture', profilePicture); 75 | 76 | Auth.UserData expectedUserData = new Auth.UserData( id, 77 | null, // firstName 78 | null, // lastName 79 | fullName, // fullname 80 | null,// email 81 | website, // link 82 | userName, 83 | null, // locale 84 | 'Instagram', 85 | null, // siteLoginUrl 86 | attributeMap); 87 | System.assertEquals(expectedUserData.fullname, actualUserData.fullname); 88 | System.assertEquals(expectedUserData.userName, actualUserData.userName); 89 | System.assertEquals(expectedUserData.link, actualUserData.link); 90 | System.assertEquals(expectedUserData.attributeMap.get('profile_picture'), actualUserData.attributeMap.get('profile_picture')); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /customMetadata/Instagram_Identity.Instagram.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | Access_Token_URL__c 7 | https://api.instagram.com/oauth/access_token/ 8 | 9 | 10 | Auth_URL__c 11 | https://api.instagram.com/oauth/authorize/ 12 | 13 | 14 | UserEndpoint__c 15 | https://api.instagram.com/v1/users/self/ 16 | 17 | 18 | clientID__c 19 | b1ad899b52fd4d8c920e775602ad8891 20 | 21 | 22 | clientSecret__c 23 | cb2016a1a1b24d2db362fab7115ea898 24 | 25 | 26 | redirectURL__c 27 | https://login.salesforce.com/services/authcallback/00Do0000000KrrsEAC/Instagram 28 | 29 | 30 | -------------------------------------------------------------------------------- /layouts/Instagram_Identity__mdt-Instagram Identity Layout.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | 8 | 9 | 10 | Required 11 | MasterLabel 12 | 13 | 14 | Required 15 | DeveloperName 16 | 17 | 18 | Edit 19 | clientID__c 20 | 21 | 22 | Edit 23 | clientSecret__c 24 | 25 | 26 | Edit 27 | Auth_URL__c 28 | 29 | 30 | Edit 31 | Access_Token_URL__c 32 | 33 | 34 | Edit 35 | UserEndpoint__c 36 | 37 | 38 | Edit 39 | redirectURL__c 40 | 41 | 42 | 43 | 44 | Edit 45 | IsProtected 46 | 47 | 48 | Required 49 | NamespacePrefix 50 | 51 | 52 | 53 | 54 | 55 | false 56 | false 57 | true 58 | 59 | 60 | 61 | Readonly 62 | CreatedById 63 | 64 | 65 | 66 | 67 | Readonly 68 | LastModifiedById 69 | 70 | 71 | 72 | 73 | 74 | false 75 | false 76 | false 77 | 78 | 79 | 80 | false 81 | false 82 | false 83 | false 84 | false 85 | 86 | -------------------------------------------------------------------------------- /objects/Instagram_Identity__mdt.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Access_Token_URL__c 5 | "https://api.instagram.com/oauth/access_token/" 6 | false 7 | SubscriberControlled 8 | 9 | false 10 | Url 11 | 12 | 13 | Auth_URL__c 14 | "https://api.instagram.com/oauth/authorize/" 15 | false 16 | SubscriberControlled 17 | 18 | false 19 | Url 20 | 21 | 22 | UserEndpoint__c 23 | "https://api.instagram.com/v1/users/self/" 24 | false 25 | SubscriberControlled 26 | 27 | false 28 | Url 29 | 30 | 31 | clientID__c 32 | false 33 | SubscriberControlled 34 | 35 | 255 36 | false 37 | Text 38 | false 39 | 40 | 41 | clientSecret__c 42 | false 43 | SubscriberControlled 44 | 45 | 255 46 | false 47 | Text 48 | false 49 | 50 | 51 | redirectURL__c 52 | false 53 | SubscriberControlled 54 | 55 | false 56 | Url 57 | 58 | 59 | Instragram Identities 60 | Public 61 | 62 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | instagram 4 | 5 | Instagram 6 | InstagramMockHttpResponseGenerator 7 | InstagramRegHandler 8 | InstagramRegHandlerTestClass 9 | InstagramTestClass 10 | ApexClass 11 | 12 | 13 | Instagram_Identity__mdt.Access_Token_URL__c 14 | Instagram_Identity__mdt.Auth_URL__c 15 | Instagram_Identity__mdt.UserEndpoint__c 16 | Instagram_Identity__mdt.clientID__c 17 | Instagram_Identity__mdt.clientSecret__c 18 | Instagram_Identity__mdt.redirectURL__c 19 | CustomField 20 | 21 | 22 | Instagram_Identity.Instagram 23 | CustomMetadata 24 | 25 | 26 | Instagram_Identity__mdt 27 | CustomObject 28 | 29 | 30 | Instagram_Identity__mdt-Instagram Identity Layout 31 | Layout 32 | 33 | 37.0 34 | 35 | --------------------------------------------------------------------------------