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