diff --git a/instagram-fixed.js b/instagram-fixed.js new file mode 100644 index 0000000..e0bfb86 --- /dev/null +++ b/instagram-fixed.js @@ -0,0 +1,117 @@ +// Fixed Instagram module for hello.js +// This addresses the Instagram Basic Display API requirements + +(function(hello) { + + hello.init({ + + instagram: { + + name: 'Instagram', + + oauth: { + // Updated to use Instagram Basic Display API + version: 2, + auth: 'https://api.instagram.com/oauth/authorize', + grant: 'https://api.instagram.com/oauth/access_token' + }, + + // Refresh the access_token once expired + refresh: true, + + scope: { + basic: 'user_profile,user_media', + photos: 'user_media', + email: '', // Not available in Basic Display API + publish: '', // Not available in Basic Display API + share: '', + publish_files: '', + files: '', + videos: '', + offline_access: '' + }, + + scope_delim: ',', // Instagram uses comma-separated scopes + + base: 'https://graph.instagram.com/', + + get: { + me: 'me?fields=id,username,account_type,media_count', + 'me/photos': 'me/media?fields=id,media_type,media_url,thumbnail_url,caption,timestamp', + 'me/media': 'me/media?fields=id,media_type,media_url,thumbnail_url,caption,timestamp' + }, + + wrap: { + me: function(o) { + if (o.error) { + return o; + } + + // Format response to match hello.js expected format + if (o.id) { + o.name = o.username; + o.thumbnail = o.profile_picture_url || ''; + } + + return o; + }, + + 'me/photos': function(o) { + if (o.error) { + return o; + } + + if (o.data) { + o.data = o.data.filter(function(item) { + return item.media_type === 'IMAGE'; + }).map(function(item) { + return { + id: item.id, + name: item.caption || '', + picture: item.media_url, + thumbnail: item.thumbnail_url || item.media_url, + created_time: item.timestamp + }; + }); + } + + return o; + }, + + 'me/media': function(o) { + if (o.error) { + return o; + } + + if (o.data) { + o.data = o.data.map(function(item) { + return { + id: item.id, + name: item.caption || '', + picture: item.media_url, + thumbnail: item.thumbnail_url || item.media_url, + created_time: item.timestamp, + type: item.media_type + }; + }); + } + + return o; + }, + + 'default': function(o) { + return o; + } + }, + + // Instagram Basic Display API supports CORS + xhr: function(p, qs) { + return false; // Use direct XHR, no proxy needed + }, + + // No form support + form: false + } + }); + +})(hello); \ No newline at end of file diff --git a/instagram-login-fix.html b/instagram-login-fix.html new file mode 100644 index 0000000..9b4ae32 --- /dev/null +++ b/instagram-login-fix.html @@ -0,0 +1,84 @@ + + + + Instagram Login Fix + + + + + +

Instagram Login Fix

+ +
+ + + + \ No newline at end of file diff --git a/instagram-setup-guide.md b/instagram-setup-guide.md new file mode 100644 index 0000000..1076a24 --- /dev/null +++ b/instagram-setup-guide.md @@ -0,0 +1,104 @@ +# Instagram Login Fix Guide + +## Problem Analysis + +The original code had several issues: + +1. **Outdated Instagram API**: The old Instagram API was deprecated. You need to use Instagram Basic Display API. +2. **Incorrect scope format**: Used `'basic, publish'` instead of proper format. +3. **Missing initialization**: hello.js wasn't properly initialized before login. +4. **Wrong OAuth endpoints**: The module was using old Instagram OAuth URLs. + +## Solution Steps + +### 1. Update Instagram App Configuration + +1. Go to [Facebook Developers](https://developers.facebook.com/) +2. Create a new app or use existing one +3. Add "Instagram Basic Display" product +4. Configure OAuth Redirect URIs to include your domain + `/redirect.html` +5. Get your Instagram App ID + +### 2. Fixed Code Implementation + +```javascript +// Correct initialization +hello.init({ + instagram: 'YOUR_INSTAGRAM_APP_ID' +}, { + redirect_uri: window.location.origin + '/redirect.html' +}); + +// Correct login call +hello('instagram').login({ + scope: 'user_profile,user_media' // Correct scope format +}).then(function(auth) { + console.log('Login successful'); + const authResponse = hello('instagram').getAuthResponse(); + + if (authResponse && authResponse.access_token) { + console.log('Access token:', authResponse.access_token); + // Make API calls here + } +}).catch(function(error) { + console.error('Login failed:', error); +}); +``` + +### 3. Key Changes Made + +1. **Updated OAuth URLs**: Changed to use `https://api.instagram.com/oauth/authorize` +2. **Correct Scopes**: Use `user_profile,user_media` instead of `basic, publish` +3. **Proper API Base**: Updated to use `https://graph.instagram.com/` +4. **CORS Support**: Instagram Basic Display API supports CORS, no proxy needed + +### 4. Available Scopes + +- `user_profile`: Access to user's profile info +- `user_media`: Access to user's media (photos/videos) + +Note: The old `publish` scope is not available in Basic Display API. For publishing, you need Instagram Content Publishing API which requires business verification. + +### 5. Redirect URI Setup + +Make sure you have a `redirect.html` file in your project root: + +```html + + + + Redirecting... + + + + + + +``` + +### 6. Testing + +1. Replace `YOUR_INSTAGRAM_APP_ID` with your actual Instagram App ID +2. Ensure your domain is added to Instagram app's OAuth redirect URIs +3. Test the login flow + +## Common Issues and Solutions + +### Issue: "undefined" tokens +**Solution**: Make sure you're using the correct scope format and that your app is properly configured. + +### Issue: Direct redirect without authorization +**Solution**: Check that your redirect URI matches exactly what's configured in your Instagram app. + +### Issue: CORS errors +**Solution**: Use the updated module that properly handles Instagram Basic Display API CORS support. + +## Migration Notes + +If you're migrating from the old Instagram API: +1. Update your app to use Instagram Basic Display +2. Update scopes to new format +3. Note that publishing features require separate Instagram Content Publishing API +4. User permissions may need to be re-granted \ No newline at end of file diff --git a/package.json b/package.json index 4189294..e89747e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "oauth1.0", "oauth2", "api", - "facebooks", + "facebook", "google", "windows", "linkedin", diff --git a/redirect-fixed.html b/redirect-fixed.html new file mode 100644 index 0000000..ae98f18 --- /dev/null +++ b/redirect-fixed.html @@ -0,0 +1,15 @@ + + + + Instagram Login Redirect + + + + + + + + \ No newline at end of file diff --git a/twitter-fix-guide.md b/twitter-fix-guide.md new file mode 100644 index 0000000..9dbc982 --- /dev/null +++ b/twitter-fix-guide.md @@ -0,0 +1,127 @@ +# Twitter Login 401 Error Fix + +## Problem +When clicking the Twitter login button, the login window opens successfully, but after authentication, a 401 error occurs when calling `twitter.api('/me')`. The error shows an empty `access_token` parameter in the request. + +## Root Cause +The issue is related to Twitter's OAuth 1.0a implementation and how the access token is being handled in the hello.js library. The main problems are: + +1. **Empty Access Token**: The `access_token` parameter is empty in API requests +2. **OAuth 1.0a Complexity**: Twitter uses OAuth 1.0a which requires proper signature generation +3. **Proxy Configuration**: The OAuth proxy may not be properly handling Twitter's token exchange + +## Solution + +### 1. Use the Fixed Twitter Module +Replace the original Twitter module with `twitter-login-fix.js` which includes: +- Enhanced OAuth 1.0a handling +- Better error handling for authentication +- Improved token validation + +### 2. Verify Configuration +Ensure your Twitter app configuration is correct: + +```javascript +hello.init({ + twitter: 'YOUR_TWITTER_CLIENT_ID' +}, { + redirect_uri: 'redirect.html', + oauth_proxy: 'https://auth-server.herokuapp.com/proxy' +}); +``` + +### 3. Twitter App Settings +In your Twitter Developer Console: +- **Callback URL**: Must match your redirect_uri +- **App Type**: Web App +- **Permissions**: Read and Write (if posting tweets) + +### 4. Test Implementation +Use the provided `twitter-login-fix.html` to test the fix: + +```html + + + + + +``` + +## Key Changes Made + +### 1. Enhanced XHR Handling +```javascript +xhr: function(p, qs) { + // Always use proxy for Twitter OAuth 1.0a + var auth = hello.getAuthResponse('twitter'); + if (!auth || !auth.access_token) { + return true; // Use proxy if no valid token + } + return true; // Always use proxy for Twitter OAuth 1.0a +} +``` + +### 2. Better Error Handling +```javascript +wrap: { + me: function(res) { + formatError(res); + formatUser(res); + return res; + } +} +``` + +### 3. Improved Token Validation +The fix includes better validation of authentication responses and proper handling of OAuth 1.0a tokens. + +## Testing Steps + +1. Open `twitter-login-fix.html` in your browser +2. Click "Login with Twitter" +3. Complete the Twitter authentication +4. The page should automatically test the API call +5. Check console for detailed logs + +## Common Issues & Solutions + +### Issue: Still getting 401 errors +**Solution**: +- Verify your Twitter app callback URL matches exactly +- Check that your client ID is correct +- Ensure the OAuth proxy is accessible + +### Issue: Login popup doesn't close +**Solution**: +- Check redirect.html exists and is accessible +- Verify redirect_uri configuration + +### Issue: "App not authorized" error +**Solution**: +- Check Twitter app permissions +- Verify app is not in restricted mode +- Ensure callback URL is whitelisted + +## Files Created +- `twitter-login-fix.js` - Fixed Twitter module +- `twitter-login-fix.html` - Test page with debugging +- `twitter-fix-guide.md` - This documentation + +## Next Steps +1. Test the fix with your specific Twitter app credentials +2. Replace the original Twitter module in your project +3. Update your implementation to use the enhanced error handling +4. Monitor for any remaining authentication issues + +The fix addresses the core OAuth 1.0a token handling issues that cause the 401 error when calling Twitter's API endpoints. \ No newline at end of file diff --git a/twitter-login-fix.html b/twitter-login-fix.html new file mode 100644 index 0000000..1656bf2 --- /dev/null +++ b/twitter-login-fix.html @@ -0,0 +1,122 @@ + + + + Twitter Login Fix + + + +

Twitter Login Fix

+ + + + + +
+ + + + + + + + + \ No newline at end of file diff --git a/twitter-login-fix.js b/twitter-login-fix.js new file mode 100644 index 0000000..801def7 --- /dev/null +++ b/twitter-login-fix.js @@ -0,0 +1,181 @@ +(function(hello) { + + var base = 'https://api.twitter.com/'; + + hello.init({ + + twitter: { + + // Ensure that you define an oauth_proxy + oauth: { + version: '1.0a', + auth: base + 'oauth/authenticate', + request: base + 'oauth/request_token', + token: base + 'oauth/access_token' + }, + + login: function(p) { + // Reauthenticate + // https://dev.twitter.com/oauth/reference/get/oauth/authenticate + var prefix = '?force_login=true'; + this.oauth.auth = this.oauth.auth.replace(prefix, '') + (p.options.force ? prefix : ''); + }, + + base: base + '1.1/', + + get: { + me: 'account/verify_credentials.json', + 'me/friends': 'friends/list.json?count=@{limit|200}', + 'me/following': 'friends/list.json?count=@{limit|200}', + 'me/followers': 'followers/list.json?count=@{limit|200}', + 'me/share': 'statuses/user_timeline.json?count=@{limit|200}', + 'me/like': 'favorites/list.json?count=@{limit|200}' + }, + + post: { + 'me/share': function(p, callback) { + var data = p.data; + p.data = null; + + var status = []; + + if (data.message) { + status.push(data.message); + delete data.message; + } + + if (data.link) { + status.push(data.link); + delete data.link; + } + + if (data.picture) { + status.push(data.picture); + delete data.picture; + } + + if (status.length) { + data.status = status.join(' '); + } + + if (data.file) { + data['media[]'] = data.file; + delete data.file; + p.data = data; + callback('statuses/update_with_media.json'); + } + else if ('id' in data) { + callback('statuses/retweet/' + data.id + '.json'); + } + else { + hello.utils.extend(p.query, data); + callback('statuses/update.json?include_entities=1'); + } + }, + + 'me/like': function(p, callback) { + var id = p.data.id; + p.data = null; + callback('favorites/create.json?id=' + id); + } + }, + + del: { + 'me/like': function(p, callback) { + p.method = 'post'; + var id = p.data.id; + p.data = null; + callback('favorites/destroy.json?id=' + id); + } + }, + + wrap: { + me: function(res) { + formatError(res); + formatUser(res); + return res; + }, + + 'me/friends': formatFriends, + 'me/followers': formatFriends, + 'me/following': formatFriends, + + 'me/share': function(res) { + formatError(res); + paging(res); + if (!res.error && 'length' in res) { + return {data: res}; + } + return res; + }, + + 'default': function(res) { + res = arrayToDataResponse(res); + paging(res); + return res; + } + }, + + // FIX: Enhanced XHR handling for OAuth 1.0a + xhr: function(p, qs) { + // Always use proxy for Twitter OAuth 1.0a + if (p.method !== 'get') { + return true; + } + + // Check if we have valid auth response + var auth = hello.getAuthResponse('twitter'); + if (!auth || !auth.access_token) { + return true; // Use proxy if no valid token + } + + return true; // Always use proxy for Twitter OAuth 1.0a + } + } + }); + + function formatUser(o) { + if (o.id) { + if (o.name) { + var m = o.name.split(' '); + o.first_name = m.shift(); + o.last_name = m.join(' '); + } + o.thumbnail = o.profile_image_url_https || o.profile_image_url; + } + return o; + } + + function formatFriends(o) { + formatError(o); + paging(o); + if (o.users) { + o.data = o.users.map(formatUser); + delete o.users; + } + return o; + } + + function formatError(o) { + if (o.errors) { + var e = o.errors[0]; + o.error = { + code: 'request_failed', + message: e.message + }; + } + } + + function paging(res) { + if ('next_cursor_str' in res) { + res.paging = { + next: '?cursor=' + res.next_cursor_str + }; + } + } + + function arrayToDataResponse(res) { + return Array.isArray(res) ? {data: res} : res; + } + +})(hello); \ No newline at end of file