Office365 如何访问多个用户';通过自定义守护进程和graph REST API创建OneDrive文件?

Office365 如何访问多个用户';通过自定义守护进程和graph REST API创建OneDrive文件?,office365,onedrive,microsoft-graph-api,office365-restapi,Office365,Onedrive,Microsoft Graph Api,Office365 Restapi,我们正在开发一个守护进程服务,该服务将定期自动连接到Microsoft Graph API,以列出所有用户驱动器中包含敏感内容的任何文件。我们在Azure/Office365租户帐户中设置了一个自定义应用程序,该应用程序启用了许多特权(为了测试,所有Graph和Sharepoint Priv(加上一些其他特权)) 使用Graph Explorer工具和我的个人登录帐户,我可以使用/me/drive/root/children端点和/users(“”)/drive/root/children端点(

我们正在开发一个守护进程服务,该服务将定期自动连接到Microsoft Graph API,以列出所有用户驱动器中包含敏感内容的任何文件。我们在Azure/Office365租户帐户中设置了一个自定义应用程序,该应用程序启用了许多特权(为了测试,所有Graph和Sharepoint Priv(加上一些其他特权))

使用Graph Explorer工具和我的个人登录帐户,我可以使用
/me/drive/root/children
端点和
/users(“”)/drive/root/children
端点(当用户id为我自己时)列出我自己的驱动器帐户中的文件。当我尝试使用curl和
grant\u类型的
client\u凭据连接时,使用Azure中自定义应用程序中的
client\u id
client\u secret
/users(“”)/drive
返回正确的驱动器id,但
/users(“”)/drive/root/children
只返回一个空的子驱动器列表

是否有一些我错过的许可,我们需要设置在某个地方

这是Graph API当前状态的限制吗


这是对
客户端\u凭据
授权类型的限制吗?

这是对Graph API当前状态的限制-不存在仅用于客户端凭据流的应用程序权限范围,这将允许应用程序访问任何用户的驱动器/文件。文件。*作用域只能用作委托权限-请参阅。

通过使用新的并遵循说明,现在(使用应用程序权限)就可以这样做。或者,如果您在Azure门户中创建(注册)了应用程序,则必须使用X509证书,而不是共享机密(客户端机密)。至少对我来说,最有用的资源是:

下面是一些python代码(对于第二种情况),用于生成供用户访问的url,以便用户可以授权您的应用程序,并用于请求访问令牌:

import calendar
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from datetime import datetime, timedelta
import jwt
from jwt.exceptions import InvalidTokenError
from oauthlib.common import generate_nonce, generate_token
from oauthlib.oauth2 import BackendApplicationClient
import requests
from requests_oauthlib import OAuth2Session
import uuid

def to_unix(obj):
    if isinstance(obj, datetime):
        if obj.utcoffset() is not None:
            obj = obj - obj.utcoffset()
    millis = calendar.timegm(obj.timetuple()) + obj.microsecond / 1e6
    return millis

def validate_id_token(token):
    '''Validates the given id token.

    Args:
        token (str): An encoded ID token.
    Returns:
        The decoded token which is a dict.
    '''
    # Extract kid from token header
    try:
        header = jwt.get_unverified_header(token)
    except InvalidTokenError as e:
        raise Exception('No valid id token provided.')
        })
    else:
        kid = header.get('kid', '')

    if not kid:
        raise Exception("Unable to find 'kid' claim in token header.")

    # Fetch public key info
    url = 'https://login.microsoftonline.com/common/discovery/keys'
    try:
        response = requests.get(url)
    except RequestException as e:
        raise Exception('Failed to get public key info: %s' % e)
    else:
        if not response.ok:
            raise Exception('Failed to get public key info: %s' %
                              response.content)
        else:
            public_keys = response.json().get('keys', [])

    # Find public key, used to sign id token
    public_key = None
    for k in public_keys:
        if kid == k['kid']:
            public_key = k['x5c'][0]
            break
    if not public_key:
        raise Exception("Unable to find public key for given kid '%s'" % kid)

    # Verify id token signature
    # NOTE: The x5c value is actually a X509 certificate. The public key
    # could also be generated from the n (modulos) and e (exponent) values.
    # But that's more involved.
    cert_string = ('-----BEGIN CERTIFICATE-----\n' +
                   public_key +
                   '\n-----END CERTIFICATE-----').encode('UTF-8')
    try:
        cert = x509.load_pem_x509_certificate(
            cert_string, default_backend())
    except ValueError as e:
        raise Exception('Failed to load certificate for token signature'
                          'verification: %s' % e)
    else:
        public_key = cert.public_key()

    try:
        decoded = jwt.decode(token, public_key, audience=self.key)
    except InvalidTokenError as e:
        raise Exception('Failed to decode token: %s' % e)
    else:
        return decoded

def generate_client_assertion(tenant_id, fp_hash, private_key, private_key_passphrase):
    """Generate a client assertion (jwt token).

    This token is required to fetch an oauth app-only access token.

    Args:
        fp_hash (str): Base64 encoded SHA1 has of certificate fingerprint
        private_key (str): Private key used to sign the jwt token
        tenant_id (str): The tenant to which this token is bound.
    Returns:
        On success a tuple of the client assertion and the token type
        indicator.
    """
    valid_from = str(int(ts.to_unix(datetime.utcnow() - timedelta(0, 1))))
    expires_at = str(int(ts.to_unix(datetime.utcnow() + timedelta(7))))
    jwt_payload = {
        'aud': ('https://login.microsoftonline.com/%s/'
                'oauth2/token' % tenant_id),
        'iss': client_id,
        'sub': client_id,
        'jti': str(uuid.uuid1()),
        'nbf': valid_from,
        'exp': expires_at,
    }
    headers = {
        'x5t': fp_hash
    }

    if not private_key_passphrase:
        secret = private_key
    else:
        try:
            secret = serialization.load_pem_private_key(
                str(private_key), password=str(private_key_passphrase),
                backend=default_backend())
        except Exception as e:
            raise Exception('Failed to load private key: %s' % e)

    try:
        client_assertion = jwt.encode(jwt_payload, secret,
                                      algorithm='RS256', headers=headers)
    except ValueError as e:
        raise Exception('Failed to encode jwt_payload: %s' % e)

    client_assertion_type = ('urn:ietf:params:oauth:client-assertion-type:'
                             'jwt-bearer')

    return client_assertion, client_assertion_type

def generate_auth_url(client_id, redirect_uri):
    nonce = generate_nonce()
    state = generate_token()
    query_params = {
        'client_id': client_id,
        'nonce': nonce,
        'prompt': 'admin_consent',
        'redirect_uri': redirect_uri,
        'response_mode': 'fragment',
        'response_type': 'id_token',
        'scope': 'openid',
        'state': state
    }
    tenant = 'common'
    auth_url = ('https://login.microsoftonline.com/%s'
                '/oauth2/authorize?%s') % (tenant, urllib.urlencode(query_params))

    return nonce, auth_url

def get_access_token(client_id, id_token, nonce=None):
    '''id_token is returned w/ the url after the user authorized the app'''
    decoded_id_token = validate_id_token(id_token)

    # Compare the nonce values, to mitigate token replay attacks
    if not nonce:
        raise Exception("No nonce value provided.")
    elif nonce != decoded_id_token['nonce']:
        raise Exception("Nonce values don't match!")

    # Prepare the JWT token for fetching an access token
    tenant_id = decoded_id_token['tid']
    client_assertion, client_assertion_type = generate_client_assertion(tenant_id)

    # Fetch the access token
    client = BackendApplicationClient(self.key)
    oauth = OAuth2Session(client=client)
    resource = 'https://graph.microsoft.com/'
    url = https://login.microsoftonline.com/common/oauth2/token
    query_params = {
        'client_id': client_id,
        'client_assertion': client_assertion,
        'client_assertion_type': client_assertion_type,
        'resource': resource
    }
    try:
        fetch_token_response = oauth.fetch_token(url, **query_params)
    except Exception as e:
        raise Exception('Failed to obtain access token: %s' % e)
    else:
        return fetch_token_response