{
  "name": "WF05-Google-OAuth",
  "id": "oauth-google-flow",
  "description": "Google OAuth \uc778\uc99d \uc6cc\ud06c\ud50c\ub85c\uc6b0 - MAIPatent \uc0ac\uc6a9\uc790 \uc778\uc99d \ucc98\ub9ac",
  "active": false,
  "nodes": [
    {
      "id": "oauth-start",
      "name": "OAuth \uc2dc\uc791",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "parameters": {
        "httpMethod": "GET",
        "path": "auth/google",
        "responseMode": "responseNode",
        "options": {
          "rawBody": false
        }
      }
    },
    {
      "id": "build-google-url",
      "name": "Google OAuth URL \uc0dd\uc131",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ],
      "parameters": {
        "jsCode": "// Google OAuth URL \uc0dd\uc131\nconst query = $input.first().json.query || {};\nconst callbackUrl = query.callback_url || 'https://jini92.github.io/MAIPatent/';\n\n// n8n\uc758 Google OAuth \ucf5c\ubc31 URL\nconst n8nCallbackUrl = `${$env.N8N_WEBHOOK_URL}/auth/google/callback`;\n\n// Google OAuth \uc124\uc815\nconst clientId = $env.GOOGLE_CLIENT_ID;\nconst redirectUri = encodeURIComponent(n8nCallbackUrl);\nconst scope = encodeURIComponent('openid email profile');\nconst state = Buffer.from(JSON.stringify({ callback_url: callbackUrl })).toString('base64');\n\nconst googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=${scope}&state=${state}&access_type=offline&prompt=consent`;\n\nreturn [{\n  json: {\n    googleAuthUrl,\n    callbackUrl,\n    state\n  }\n}];"
      }
    },
    {
      "id": "redirect-google",
      "name": "Google\ub85c \ub9ac\ub2e4\uc774\ub809\ud2b8",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        440,
        0
      ],
      "parameters": {
        "respondWith": "redirect",
        "redirectURL": "={{ $json.googleAuthUrl }}"
      }
    },
    {
      "id": "oauth-callback",
      "name": "OAuth \ucf5c\ubc31",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        200
      ],
      "parameters": {
        "httpMethod": "GET",
        "path": "auth/google/callback",
        "responseMode": "responseNode",
        "options": {
          "rawBody": false
        }
      }
    },
    {
      "id": "extract-code",
      "name": "\uc778\uc99d \ucf54\ub4dc \ucd94\ucd9c",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        200
      ],
      "parameters": {
        "jsCode": "// URL \ud30c\ub77c\ubbf8\ud130\uc5d0\uc11c \ucf54\ub4dc\uc640 state \ucd94\ucd9c\nconst query = $input.first().json.query || {};\nconst code = query.code;\nconst state = query.state;\nconst error = query.error;\n\nif (error) {\n  return [{\n    json: {\n      success: false,\n      error: error,\n      error_description: query.error_description || 'OAuth \uc778\uc99d\uc774 \ucde8\uc18c\ub418\uc5c8\uc2b5\ub2c8\ub2e4.'\n    }\n  }];\n}\n\nif (!code) {\n  return [{\n    json: {\n      success: false,\n      error: 'missing_code',\n      error_description: '\uc778\uc99d \ucf54\ub4dc\uac00 \uc5c6\uc2b5\ub2c8\ub2e4.'\n    }\n  }];\n}\n\n// state\uc5d0\uc11c callback_url \ucd94\ucd9c\nlet callbackUrl = 'https://jini92.github.io/MAIPatent/';\ntry {\n  const stateData = JSON.parse(Buffer.from(state, 'base64').toString());\n  callbackUrl = stateData.callback_url || callbackUrl;\n} catch (e) {\n  // state \ud30c\uc2f1 \uc2e4\ud328 \uc2dc \uae30\ubcf8\uac12 \uc0ac\uc6a9\n}\n\nreturn [{\n  json: {\n    success: true,\n    code,\n    callbackUrl\n  }\n}];"
      }
    },
    {
      "id": "check-code",
      "name": "\ucf54\ub4dc \ud655\uc778",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        440,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "success-check",
              "leftValue": "={{ $json.success }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      }
    },
    {
      "id": "exchange-token",
      "name": "\ud1a0\ud070 \uad50\ud658",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        100
      ],
      "parameters": {
        "method": "POST",
        "url": "https://oauth2.googleapis.com/token",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"code\": \"{{ $json.code }}\",\n  \"client_id\": \"{{ $env.GOOGLE_CLIENT_ID }}\",\n  \"client_secret\": \"{{ $env.GOOGLE_CLIENT_SECRET }}\",\n  \"redirect_uri\": \"{{ $env.N8N_WEBHOOK_URL }}/auth/google/callback\",\n  \"grant_type\": \"authorization_code\"\n}",
        "options": {
          "response": {
            "response": {
              "fullResponse": false,
              "responseFormat": "json"
            }
          }
        }
      }
    },
    {
      "id": "get-user-info",
      "name": "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc870\ud68c",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        100
      ],
      "parameters": {
        "method": "GET",
        "url": "https://www.googleapis.com/oauth2/v2/userinfo",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $json.access_token }}"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "build-redirect",
      "name": "\ucf5c\ubc31 URL \uc0dd\uc131",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        100
      ],
      "parameters": {
        "jsCode": "// \uc0ac\uc6a9\uc790 \uc815\ubcf4\uc640 \ucf5c\ubc31 URL \uc900\ube44\nconst userInfo = $input.first().json;\nconst callbackUrl = $('\ucf54\ub4dc \ud655\uc778').first().json.callbackUrl || 'https://jini92.github.io/MAIPatent/auth/callback/';\n\n// \uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c URL \ud30c\ub77c\ubbf8\ud130\ub85c \uc778\ucf54\ub529\nconst params = new URLSearchParams({\n  user_id: userInfo.id || '',\n  user_name: userInfo.name || '',\n  user_email: userInfo.email || '',\n  user_image: userInfo.picture || ''\n});\n\nconst redirectUrl = `${callbackUrl}?${params.toString()}`;\n\nreturn [{\n  json: {\n    redirectUrl,\n    userInfo\n  }\n}];"
      }
    },
    {
      "id": "redirect-success",
      "name": "\ud504\ub860\ud2b8\uc5d4\ub4dc\ub85c \ub9ac\ub2e4\uc774\ub809\ud2b8",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1320,
        100
      ],
      "parameters": {
        "respondWith": "redirect",
        "redirectURL": "={{ $json.redirectUrl }}"
      }
    },
    {
      "id": "build-error-redirect",
      "name": "\uc5d0\ub7ec URL \uc0dd\uc131",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        300
      ],
      "parameters": {
        "jsCode": "// \uc5d0\ub7ec \ub9ac\ub2e4\uc774\ub809\ud2b8 URL \uc0dd\uc131\nconst errorInfo = $input.first().json;\nconst callbackUrl = errorInfo.callbackUrl || 'https://jini92.github.io/MAIPatent/auth/callback/';\n\nconst params = new URLSearchParams({\n  error: errorInfo.error || 'unknown_error',\n  error_description: errorInfo.error_description || '\uc54c \uc218 \uc5c6\ub294 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.'\n});\n\nconst redirectUrl = `${callbackUrl}?${params.toString()}`;\n\nreturn [{\n  json: {\n    redirectUrl\n  }\n}];"
      }
    },
    {
      "id": "redirect-error",
      "name": "\uc5d0\ub7ec \ub9ac\ub2e4\uc774\ub809\ud2b8",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        880,
        300
      ],
      "parameters": {
        "respondWith": "redirect",
        "redirectURL": "={{ $json.redirectUrl }}"
      }
    }
  ],
  "connections": {
    "OAuth \uc2dc\uc791": {
      "main": [
        [
          {
            "node": "Google OAuth URL \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google OAuth URL \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "Google\ub85c \ub9ac\ub2e4\uc774\ub809\ud2b8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OAuth \ucf5c\ubc31": {
      "main": [
        [
          {
            "node": "\uc778\uc99d \ucf54\ub4dc \ucd94\ucd9c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc778\uc99d \ucf54\ub4dc \ucd94\ucd9c": {
      "main": [
        [
          {
            "node": "\ucf54\ub4dc \ud655\uc778",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucf54\ub4dc \ud655\uc778": {
      "main": [
        [
          {
            "node": "\ud1a0\ud070 \uad50\ud658",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\uc5d0\ub7ec URL \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud1a0\ud070 \uad50\ud658": {
      "main": [
        [
          {
            "node": "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc0ac\uc6a9\uc790 \uc815\ubcf4 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\ucf5c\ubc31 URL \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucf5c\ubc31 URL \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "\ud504\ub860\ud2b8\uc5d4\ub4dc\ub85c \ub9ac\ub2e4\uc774\ub809\ud2b8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc5d0\ub7ec URL \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "\uc5d0\ub7ec \ub9ac\ub2e4\uc774\ub809\ud2b8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner"
  },
  "meta": {
    "version": "1.0.0",
    "created": "2026-01-12",
    "updated": "2026-01-12",
    "project": "MAIPatent",
    "workflow_sequence": 5,
    "description": "Google OAuth \uc778\uc99d \ucc98\ub9ac",
    "required_credentials": [
      "GOOGLE_CLIENT_ID",
      "GOOGLE_CLIENT_SECRET"
    ],
    "endpoints": [
      "GET /auth/google - OAuth \uc2dc\uc791",
      "GET /auth/google/callback - OAuth \ucf5c\ubc31"
    ]
  }
}